Navigation Menu

Skip to content

Commit

Permalink
Support for Vimeo video feeds (bug 1529)
Browse files Browse the repository at this point in the history
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
thp committed Jan 3, 2012
1 parent 57051e7 commit 672e071
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 9 deletions.
3 changes: 3 additions & 0 deletions src/gpodder/download.py
Expand Up @@ -32,6 +32,7 @@

from gpodder import util
from gpodder import youtube
from gpodder import vimeo
import gpodder

import threading
Expand Down Expand Up @@ -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)
Expand Down
25 changes: 16 additions & 9 deletions src/gpodder/model.py
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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'):
Expand Down Expand Up @@ -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
Expand Down
93 changes: 93 additions & 0 deletions 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.