Skip to content
Browse files

Support for Vimeo video feeds (bug 1529)

This is closely modelled around the YouTube module.

In the future, we should probably remodel this to
use the extensions/hooks/plugins mechanism, and make
the interface for video services more generic.
  • Loading branch information...
1 parent 57051e7 commit 672e071843b4c4683dcbb82a7b68adfe3ffcb5ff @thp thp committed Jan 3, 2012
Showing with 112 additions and 9 deletions.
  1. +3 −0 src/gpodder/download.py
  2. +16 −9 src/gpodder/model.py
  3. +93 −0 src/gpodder/vimeo.py
View
3 src/gpodder/download.py
@@ -32,6 +32,7 @@
from gpodder import util
from gpodder import youtube
+from gpodder import vimeo
import gpodder
import threading
@@ -735,6 +736,8 @@ def run(self):
# Resolve URL and start downloading the episode
url = youtube.get_real_download_url(self.__episode.url, \
self._config.youtube_preferred_fmt_id)
+ url = vimeo.get_real_download_url(url)
+
downloader = DownloadURLOpener(self.__episode.channel)
headers, real_url = downloader.retrieve_resume(url, \
self.tempname, reporthook=self.status_updated)
View
25 src/gpodder/model.py
@@ -28,6 +28,7 @@
from gpodder import util
from gpodder import feedcore
from gpodder import youtube
+from gpodder import vimeo
from gpodder import schema
import logging
@@ -74,7 +75,9 @@ def fetch_channel(self, channel):
self.fetch(url, etag, modified)
def _resolve_url(self, url):
- return youtube.get_real_channel_url(url)
+ url = youtube.get_real_channel_url(url)
+ url = vimeo.get_real_channel_url(url)
+ return url
@classmethod
def register(cls, handler):
@@ -289,7 +292,8 @@ def calculate_preference_value(enclosure):
if not episode.url:
continue
- if youtube.is_video_link(episode.url):
+ if (youtube.is_video_link(episode.url) or \
+ vimeo.is_video_link(episode.url)):
return episode
# Check if we can resolve this link to a audio/video file
@@ -471,8 +475,8 @@ def get_playback_url(self, fmt_id=None, allow_partial=False):
if url is None or not os.path.exists(url):
url = self.url
- if youtube.is_video_link(url):
- url = youtube.get_real_download_url(url, fmt_id)
+ url = youtube.get_real_download_url(url, fmt_id)
+ url = vimeo.get_real_download_url(url)
return url
@@ -631,8 +635,8 @@ def sync_filename(self):
return self.title
def file_type(self):
- # Assume all YouTube links are video files
- if youtube.is_video_link(self.url):
+ # Assume all YouTube/Vimeo links are video files
+ if youtube.is_video_link(self.url) or vimeo.is_video_link(self.url):
return 'video'
return util.file_type_by_extension(self.extension())
@@ -952,11 +956,14 @@ def _consume_updated_feed(self, feed, max_episodes=0, mimetype_prefs=''):
self.link = feed.feed.get('link', self.link)
self.description = feed.feed.get('subtitle', self.description)
- # Start YouTube-specific title FIX
+ # Start YouTube- and Vimeo-specific title FIX
YOUTUBE_PREFIX = 'Uploads by '
+ VIMEO_PREFIX = 'Vimeo / '
if self.title.startswith(YOUTUBE_PREFIX):
self.title = self.title[len(YOUTUBE_PREFIX):] + ' on YouTube'
- # End YouTube-specific title FIX
+ elif self.title.startswith(VIMEO_PREFIX):
+ self.title = self.title[len(VIMEO_PREFIX):] + ' on Vimeo'
+ # End YouTube- and Vimeo-specific title FIX
if hasattr(feed.feed, 'image'):
for attribute in ('href', 'url'):
@@ -1162,7 +1169,7 @@ def group_by(self):
return self.section
def _get_content_type(self):
- if 'youtube.com' in self.url:
+ if 'youtube.com' in self.url or 'vimeo.com' in self.url:
return _('Video')
audio, video, other = 0, 0, 0
View
93 src/gpodder/vimeo.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# gPodder - A media aggregator and podcast client
+# Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
+#
+# gPodder 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.
+#
+# gPodder 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 <http://www.gnu.org/licenses/>.
+#
+
+#
+# gpodder.vimeo - Vimeo download magic
+# Thomas Perl <thp@gpodder.org>; 2012-01-03
+#
+
+
+import gpodder
+
+from gpodder import util
+
+import logging
+logger = logging.getLogger(__name__)
+
+import re
+
+VIMEOCOM_RE = re.compile(r'http://vimeo\.com/(\d+)$', re.IGNORECASE)
+MOOGALOOP_RE = re.compile(r'http://vimeo\.com/moogaloop\.swf\?clip_id=(\d+)$', re.IGNORECASE)
+SIGNATURE_RE = re.compile(r'"signature":"([^"]+)","timestamp":(\d+)')
+
+class VimeoError(BaseException): pass
+
+def get_real_download_url(url):
+ quality = 'sd'
+ codecs = 'H264,VP8,VP6'
+
+ video_id = get_vimeo_id(url)
+
+ if video_id is None:
+ return url
+
+ web_url = 'http://vimeo.com/%s' % video_id
+ web_data = util.urlopen(web_url).read()
+ sig_pair = SIGNATURE_RE.search(web_data)
+
+ if sig_pair is None:
+ raise VimeoError('Cannot get signature pair from Vimeo')
+
+ signature, timestamp = sig_pair.groups()
+ params = '&'.join('%s=%s' % i for i in [
+ ('clip_id', video_id),
+ ('sig', signature),
+ ('time', timestamp),
+ ('quality', quality),
+ ('codecs', codecs),
+ ('type', 'moogaloop_local'),
+ ('embed_location', ''),
+ ])
+ player_url = 'http://player.vimeo.com/play_redirect?%s' % params
+ return player_url
+
+def get_vimeo_id(url):
+ result = MOOGALOOP_RE.match(url)
+ if result is not None:
+ return result.group(1)
+
+ result = VIMEOCOM_RE.match(url)
+ if result is not None:
+ return result.group(1)
+
+ return None
+
+def is_video_link(url):
+ return (get_vimeo_id(url) is not None)
+
+def get_real_channel_url(url):
+ result = VIMEOCOM_RE.match(url)
+ if result is not None:
+ return 'http://vimeo.com/%s/videos/rss' % result.group(1)
+
+ return url
+
+def get_real_cover(url):
+ return None
+

0 comments on commit 672e071

Please sign in to comment.
Something went wrong with that request. Please try again.