media/canalplus.lua: Relicense under AGPLv3+
authorMohamed El Morabity <melmorabity@fedoraproject.org>
Mon, 11 Nov 2013 15:12:43 +0000 (11 16:12 +0100)
committerToni Gundogdu <legatvs@gmail.com>
Tue, 12 Nov 2013 14:41:43 +0000 (12 16:41 +0200)
Notable differences to the 0.4 script:
  - Supports also d8.tv and d17.tv which share the same video database
  - Uses the JSON interface provided by Canal+

Differences (:editor:) to the original patch[1]:
  - Concatenate strings using a table, or "the Lua way"[2]
  - Remove the old script from the repo
  test:
  - Update expected media {title,ID} for the first test URL
  - Rename test function to `test_media_canalplus'

Links:
[1]: http://thread.gmane.org/gmane.comp.web.flash.quvi/364
[2]: http://www.luafaq.org/#T1.9

Signed-off-by: Mohamed El Morabity <melmorabity@fedoraproject.org>
Edited-by: Toni Gundogdu <legatvs@gmail.com>
share/Makefile.am
share/lua/website/canalplus.lua [deleted file]
share/media/canalplus.lua [new file with mode: 0644]
tests/media/canalplus.mk [new file with mode: 0644]
tests/media/media_canalplus.c [new file with mode: 0644]
tests/media/tests.mk

index 7d0fc12..55d3d18 100644 (file)
@@ -46,6 +46,7 @@ dist_media_DATA=\
   media/ardmediathek.lua\
   media/audioboo.lua\
   media/bikeradar.lua\
+  media/canalplus.lua\
   media/cbsnews.lua\
   media/clipfish.lua\
   media/collegehumor.lua\
diff --git a/share/lua/website/canalplus.lua b/share/lua/website/canalplus.lua
deleted file mode 100644 (file)
index 7966c66..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-
--- libquvi-scripts
--- Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
---
--- This file is part of libquvi-scripts <http://quvi.googlecode.com/>.
---
--- This library is free software; you can redistribute it and/or
--- modify it under the terms of the GNU Lesser General Public
--- License as published by the Free Software Foundation; either
--- version 2.1 of the License, or (at your option) any later version.
---
--- This library 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
--- Lesser General Public License for more details.
---
--- You should have received a copy of the GNU Lesser General Public
--- License along with this library; if not, write to the Free Software
--- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
--- 02110-1301  USA
-
-local CanalPlus = {} -- Utility functions unique to to this script.
-
--- Identify the script.
-function ident(self)
-    package.path = self.script_dir .. '/?.lua'
-    local C      = require 'quvi/const'
-    local r      = {}
-    r.domain     = "canalplus%.fr"
-    r.formats    = "default|best"
-    r.categories = C.proto_rtmp
-    local U      = require 'quvi/util'
-    r.handles    = U.handles(self.page_url, {r.domain}, {"/pid%d",
-                    -- presidentielle2012.canalplus.fr
-                    "/emissions", "/candidats", "/debats",
-                    -- canalstreet.canalplus.fr
-                    "/musique", "/actu", "/humour", "/tendances", "/sport", "/arts", "/danse"})
-
-    return r
-end
-
--- Query available formats.
-function query_formats(self)
-    local config  = CanalPlus.get_config(self)
-
-    if #self.redirect_url >0 then
-        return self
-    end
-
-    local U       = require 'quvi/util'
-    local formats = CanalPlus.iter_formats(self, config, U)
-
-    local t = {}
-    for _,v in pairs(formats) do
-        table.insert(t, CanalPlus.to_s(v))
-    end
-
-    table.sort(t)
-    self.formats = table.concat(t, "|")
-
-    return self
-end
-
--- Parse media URL.
-function parse(self)
-    self.host_id  = 'canalplus'
-
-    local config  = CanalPlus.get_config(self)
-
-    if #self.redirect_url >0 then
-        return self
-    end
-
-    local U       = require 'quvi/util'
-    local formats = CanalPlus.iter_formats(self, config, U)
-    local format  = U.choose_format(self, formats,
-                                    CanalPlus.choose_best,
-                                    CanalPlus.choose_default,
-                                    CanalPlus.to_s)
-                        or error("unable to choose format")
-    self.url           = {format.url or error("no match: media url")}
-
-    return self
-end
-
---
--- Utility functions
---
-
-function CanalPlus.get_config(self)
-    local t    = {}
-    local page = quvi.fetch(self.page_url)
-
-    local u = page:match('"og:video" content="(.-)"')
-    if u and not u:match('canalplus%.fr') then
-        self.redirect_url = u -- Media is hosted elsewhere, e.g. YouTube.
-        return
-    end
-
-    -- canalplus.fr
-    self.title = page:match('videoTitre%s-=%s-"(.-)"')
-    if not self.title then
-      -- presidentielle2012.canalplus.fr
-      self.title = page:match('property="og:title"%s+content="(.-)"')
-                    or error('no match: media title')
-    end
-
-    self.id = page:match('videoId=(%d+)')
-                or page:match('videoId%s+=%s+"(%d+)"')
-                or error('no match: media ID')
-
-    local u = "http://service.canal-plus.com/video/rest/getVideosLiees/cplus/"
-                .. self.id
-
-    return quvi.fetch(u, {fetch_type = 'config'})
-end
-
-function CanalPlus.iter_formats(self, config, U)
-
-    local id = config:match('<ID>(.-)</ID>')
-    if id and id == '-1' then
-        error('Media is no longer available (expired)')
-    end
-
-    local p = '<ID>' .. self.id .. '</ID>'
-           .. '.-<INFOS>'
-           .. '.-<TITRAGE>'
-           .. '.-<MEDIA>'
-           .. '.-<IMAGES>'
-           .. '.-<PETIT>(.-)<'
-           .. '.-<VIDEOS>'
-           .. '.-<BAS_DEBIT>(.-)<'
-           .. '.-<HAUT_DEBIT>(.-)<'
-           .. '.-<HD>(.-)<'
-
-    -- sd = low definition flv
-    -- hd = high definition flv
-    -- hq = high definition mp4
-
-    local thumb,sd_url,hd_url,hq_url = config:match(p)
-    if not sd_url then error("no match: media url") end
-
-    self.thumbnail_url = thumb or ''
-
-    local t = {}
-    table.insert(t, {url=sd_url, quality="sd"})
-    table.insert(t, {url=hd_url, quality="hd"})
-    table.insert(t, {url=hq_url, quality="hq"})
-    return t
-end
-
-function CanalPlus.choose_default(t)
-    return t[1] -- Presume the first to be the 'default'.
-end
-
-function CanalPlus.choose_best(t)
-    return t[#t] -- Presume the last to be the 'best'.
-end
-
-function CanalPlus.to_s(t)
-    return t.quality
-end
-
--- vim: set ts=4 sw=4 tw=72 expandtab:
diff --git a/share/media/canalplus.lua b/share/media/canalplus.lua
new file mode 100644 (file)
index 0000000..f9ec4cc
--- /dev/null
@@ -0,0 +1,127 @@
+-- libquvi-scripts
+-- Copyright (C) 2013  Mohamed El Morabity <melmorabity@fedoraproject.org>
+-- Copyright (C) 2012  Paul Kocialkowski <contact@paulk.fr>
+
+-- This file is part of libquvi-scripts <http://quvi.sourceforge.net/>.
+--
+-- This program is free software: you can redistribute it and/or
+-- modify it under the terms of the GNU Affero 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 Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General
+-- Public License along with this program.  If not, see
+-- <http://www.gnu.org/licenses/>.
+--
+
+local CanalPlus = {} -- Utility functions unique to to this script.
+
+-- Identify the media script.
+function ident(qargs)
+  return {
+    can_parse_url = CanalPlus.can_parse_url(qargs),
+    domains = table.concat({'canalplus.fr', 'd8.tv', 'd17.tv'}, ',')
+  }
+end
+
+-- Parse media properties.
+function parse(qargs)
+  local J = require 'json'
+
+  local p = quvi.http.fetch(qargs.input_url).data
+
+  -- Make mandatory: needed to fetch the video data JSON.
+  qargs.id = p:match('videoId%s*=%s*"?(%d+)"?')
+               or error('no match: media ID')
+
+  -- Get the channel ID.
+  local channel = p:match('src="http://player%.canalplus%.fr/common/js/'
+                           .. 'canalPlayer%.js%?param=(.-)"')
+                                 or error('no match: channel ID')
+
+  -- Fetch the video data JSON.
+  local t = {
+     'http://service.canal-plus.com/video/rest/getVideos/', channel, '/',
+     qargs.id, '?format=json'
+  }
+
+  local c = quvi.http.fetch(table.concat(t)).data
+  j = J.decode(c)
+
+  qargs.thumb_url = j['MEDIA']['IMAGES']['GRAND']
+
+  qargs.title = j['INFOS']['TITRAGE']['TITRE']
+
+  qargs.streams = CanalPlus.iter_streams(j)
+
+  return qargs
+end
+
+--
+-- Utility functions
+--
+
+function CanalPlus.can_parse_url(qargs)
+  local U = require 'socket.url'
+  local t = U.parse(qargs.input_url)
+  if t and t.scheme and t.scheme:lower():match('^http$')
+       and t.host   and (t.host:lower():match('canalplus%.fr$')
+                         or t.host:lower():match('d8%.tv$')
+                         or t.host:lower():match('d17%.tv$'))
+       and t.path   and t.path:lower():match('/pid%d+') then
+    return true
+  else
+    return false
+  end
+end
+
+function CanalPlus.iter_streams(j)
+  local S = require 'quvi/stream'
+
+  local d = j['MEDIA']['VIDEOS']
+  local r = {}
+
+  for q, v in pairs(d) do
+    if v ~= ''
+      -- Some streams have denied accessed, just ignore them.
+      and q ~= 'HDS' and q ~= 'HLS' and q ~= 'IPHONE' and q ~= 'IPAD' then
+      local t = S.stream_new(v)
+      t.nostd = { quality = q }  -- Ignored by libquvi.
+      t.id = CanalPlus.to_id(t)
+      table.insert(r, t)
+    end
+  end
+
+  if #r >1 then
+    CanalPlus.ch_best(S, r)
+  end
+
+  return r
+end
+
+function CanalPlus.ch_best(S, t)
+  -- Available video qualities.
+  -- HD         -> High definition
+  -- HAUT_DEBIT -> Broadband access
+  -- BAS_DEBIT  -> Dial-up access
+  -- MOBILE     -> Mobile access
+  local q = { HD = 4, HAUT_DEBIT = 3, BAS_DEBIT = 2, MOBILE = 1 }
+  local r = t[1]
+  r.flags.best = true
+  for _,v in pairs(t) do
+    if q[v.nostd.quality] > q[r.nostd.quality] then
+      r = S.swap_best(r, v)
+    end
+  end
+end
+
+function CanalPlus.to_id(t)
+  return t.nostd.quality:lower()
+end
+
+-- vim: set ts=2 sw=2 tw=72 expandtab:
diff --git a/tests/media/canalplus.mk b/tests/media/canalplus.mk
new file mode 100644 (file)
index 0000000..43ae9af
--- /dev/null
@@ -0,0 +1,6 @@
+TEST_PROGS+=media_canalplus
+media_canalplus_SOURCES=media/media_canalplus.c
+media_canalplus_CPPFLAGS=$(testsuite_cppflags)
+media_canalplus_LDFLAGS=$(testsuite_ldflags)
+media_canalplus_LDADD=$(testsuite_ldadd)
+media_canalplus_CFLAGS=$(AM_CFLAGS)
diff --git a/tests/media/media_canalplus.c b/tests/media/media_canalplus.c
new file mode 100644 (file)
index 0000000..f2f437f
--- /dev/null
@@ -0,0 +1,79 @@
+/* libquvi-scripts
+ * Copyright (C) 2013  Mohamed El Morabity <melmorabity@fedoraproject.org>
+ * Copyright (C) 2013  Toni Gundogdu <legatvs@gmail.com>
+ *
+ * This file is part of libquvi-scripts <http://quvi.sourceforge.net>.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General
+ * Public License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <quvi.h>
+
+#include "tests.h"
+
+static const gchar *URLs[] =
+{
+  "http://www.canalplus.fr/c-divertissement/pid1784-c-les-guignols.html?vid=791262",
+  "http://www.d8.tv/d8-divertissement/pid5200-d8-le-grand-8.html?vid=954696",
+  "http://www.d17.tv/docs-mags/pid6273-musique.html?vid=933914",
+  NULL
+};
+
+static const gchar *TITLEs[] =
+{
+  "Les Guignols de l'Info du 11/11/13",
+  "Face à face : Amanda Lear vs Roselyne Bachelot",
+  "Pink Floyd : Behind «the wall»",
+  NULL
+};
+
+static const gchar *IDs[] =
+{
+  "969879",
+  "954696",
+  "933914",
+  NULL
+};
+
+static void test_media_canalplus()
+{
+  struct qm_test_exact_s e;
+  struct qm_test_opts_s o;
+  gint i;
+
+  for (i=0; URLs[i] != NULL; ++i)
+    {
+      memset(&e, 0, sizeof(e));
+      memset(&o, 0, sizeof(o));
+
+      e.title = TITLEs[i];
+      e.id = IDs[i];
+
+      qm_test(__func__, URLs[i], &e, &o);
+    }
+}
+
+gint main(gint argc, gchar **argv)
+{
+  g_test_init(&argc, &argv, NULL);
+  g_test_add_func("/media/canalplus", test_media_canalplus);
+  return (g_test_run());
+}
+
+/* vim: set ts=2 sw=2 tw=72 expandtab: */
index d82c1e3..e51bd0e 100644 (file)
@@ -4,6 +4,7 @@ include $(top_srcdir)/tests/media/ardmediathek.mk
 include $(top_srcdir)/tests/media/arte.mk
 include $(top_srcdir)/tests/media/audioboo.mk
 include $(top_srcdir)/tests/media/bikeradar.mk
+include $(top_srcdir)/tests/media/canalplus.mk
 include $(top_srcdir)/tests/media/cbsnews.mk
 include $(top_srcdir)/tests/media/clipfish.mk
 include $(top_srcdir)/tests/media/collegehumor.mk