def test_save_empty_logo(self): """ Make sure that save_podcast_logo(None) does not fail """ try: CoverArt.save_podcast_logo(None) except: self.fail('CoverArt.save_podcast_logo(None) should not raise ' 'an exception')
def _save_logo(self): with responses.RequestsMock() as rsps, open(IMG_PATH1, 'rb') as body: rsps.add( responses.GET, self.URL, status=200, body=body, content_type='image/png' ) CoverArt.save_podcast_logo(self.URL)
def test_exception_during_fetch(self): with responses.RequestsMock() as rsps: rsps.add(responses.GET, self.URL, body=requests.exceptions.RequestException( 'Fetching URL failed')) CoverArt.save_podcast_logo(self.URL)
def test_save_empty_logo(self): """ Make sure that save_podcast_logo(None) does not fail """ try: CoverArt.save_podcast_logo(None) except: self.fail( 'CoverArt.save_podcast_logo(None) should not raise ' 'an exception' )
def get_podcast_logo(podcast, feed): cover_art = podcast.logo_url image = feed.feed.get('image', None) if image is not None: for key in ('href', 'url'): cover_art = getattr(image, key, None) if cover_art: break if podcast.link: yturl = youtube.get_real_cover(podcast.link) if yturl: cover_art = yturl if cover_art: try: image_sha1 = hashlib.sha1(cover_art).hexdigest() prefix = CoverArt.get_prefix(image_sha1) filename = CoverArt.get_original(prefix, image_sha1) dirname = CoverArt.get_dir(filename) # get hash of existing file if os.path.exists(filename): with open(filename) as f: old_hash = file_hash(f).digest() else: old_hash = '' print 'LOGO @', cover_art # save new cover art with open(filename, 'w') as fp: fp.write(urllib2.urlopen(cover_art).read()) # get hash of new file with open(filename) as f: new_hash = file_hash(f).digest() # remove thumbnails if cover changed if old_hash != new_hash: thumbnails = CoverArt.get_existing_thumbnails(prefix, filename) print 'Removing %d thumbnails' % len(thumbnails) for f in thumbnails: os.unlink(f) return cover_art except Exception, e: if str(e).strip(): try: print >> sys.stderr, \ unicode('cannot save image for podcast %s: %s' % (podcast.get_id(), str(e)), errors='ignore') except: print >> sys.stderr, 'cannot save podcast logo' return None
def test_exception_during_fetch(self): with responses.RequestsMock() as rsps: rsps.add( responses.GET, self.URL, body=requests.exceptions.RequestException('Fetching URL failed'), ) CoverArt.save_podcast_logo(self.URL)
def get_logo_url(self, size): if self.logo_url: filename = hashlib.sha1(self.logo_url).hexdigest() else: filename = 'podcast-%d.png' % (hash(self.title) % 5, ) prefix = CoverArt.get_prefix(filename) return reverse('logo', args=[size, prefix, filename])
def _save_podcast_logo(cover_art): if not cover_art: return try: image_sha1 = hashlib.sha1(cover_art.encode('utf-8')).hexdigest() prefix = CoverArt.get_prefix(image_sha1) filename = CoverArt.get_original(prefix, image_sha1) dirname = CoverArt.get_dir(filename) # get hash of existing file if os.path.exists(filename): with open(filename) as f: old_hash = file_hash(f).digest() else: old_hash = '' logger.info('Logo %s', cover_art) # save new cover art with open(filename, 'wb') as fp: fp.write(urllib.request.urlopen(cover_art).read()) # get hash of new file with open(filename) as f: new_hash = file_hash(f).digest() # remove thumbnails if cover changed if old_hash != new_hash: thumbnails = CoverArt.get_existing_thumbnails(prefix, filename) logger.info('Removing %d thumbnails', len(thumbnails)) for f in thumbnails: os.unlink(f) return cover_art except (urllib.error.HTTPError, urllib.error.URLError, ValueError, http.client.BadStatusLine, socket.error, IOError) as e: logger.warn('Exception while updating podcast logo: %s', str(e))
def test_new_logo(self): with responses.RequestsMock() as rsps, open( IMG_PATH1, 'rb') as body1, open(IMG_PATH1, 'rb') as body2, open(IMG_PATH2, 'rb') as body3: rsps.add( responses.GET, self.URL, status=200, body=body1, content_type='image/png', ) rsps.add( responses.GET, self.URL, status=200, body=body2, content_type='image/png', ) rsps.add( responses.GET, self.URL, status=200, body=body3, content_type='image/png', ) logo_url = get_logo_url(self.podcast, 32) # first request CoverArt.save_podcast_logo(self.URL) response1 = self._fetch_cover(self.podcast) # stayed the same CoverArt.save_podcast_logo(self.URL) response2 = self._fetch_cover(self.podcast) self.assertEqual(list(response1.streaming_content), list(response2.streaming_content)) # changed CoverArt.save_podcast_logo(self.URL) response3 = self._fetch_cover(self.podcast) self.assertNotEqual(list(response2.streaming_content), list(response3.streaming_content))
def test_new_logo(self): with responses.RequestsMock() as rsps, open(IMG_PATH1, 'rb') as body1, open( IMG_PATH1, 'rb' ) as body2, open(IMG_PATH2, 'rb') as body3: rsps.add( responses.GET, self.URL, status=200, body=body1, content_type='image/png', ) rsps.add( responses.GET, self.URL, status=200, body=body2, content_type='image/png', ) rsps.add( responses.GET, self.URL, status=200, body=body3, content_type='image/png', ) logo_url = get_logo_url(self.podcast, 32) # first request CoverArt.save_podcast_logo(self.URL) response1 = self._fetch_cover(self.podcast) # stayed the same CoverArt.save_podcast_logo(self.URL) response2 = self._fetch_cover(self.podcast) self.assertEqual( list(response1.streaming_content), list(response2.streaming_content) ) # changed CoverArt.save_podcast_logo(self.URL) response3 = self._fetch_cover(self.podcast) self.assertNotEqual( list(response2.streaming_content), list(response3.streaming_content) )
import re from django.urls import path from django.conf import settings from django.views.generic.base import TemplateView, RedirectView from django.views.static import serve from mygpo.web.logo import CoverArt from . import views urlpatterns = [ path("", views.home, name="home"), path( "logo/<int:size>/<str:prefix>/<str:filename>", CoverArt.as_view(), name="logo" ), # Media files are also served in production mode. For performance, these # files should be served by a reverse proxy in practice path( "%s<path:path>" % settings.MEDIA_URL.lstrip("/"), serve, name="media", kwargs=dict(document_root=settings.MEDIA_ROOT), ), path("tags/", views.mytags, name="tags"), path( "online-help", RedirectView.as_view( url="http://gpoddernet.readthedocs.org/en/latest/user/index.html", permanent=False,
def _update_podcast(self, podcast, parsed, episode_updater, update_result): """ updates a podcast according to new parser results """ # we need that later to decide if we can "bump" a category prev_latest_episode_timestamp = podcast.latest_episode_timestamp # will later be used to see whether the index is outdated old_index_fields = get_index_fields(podcast) podcast.title = parsed.get('title') or podcast.title podcast.description = parsed.get('description') or podcast.description podcast.subtitle = parsed.get('subtitle') or podcast.subtitle podcast.link = parsed.get('link') or podcast.link podcast.logo_url = parsed.get('logo') or podcast.logo_url podcast.author = to_maxlength(Podcast, 'author', parsed.get('author') or podcast.author) podcast.language = to_maxlength( Podcast, 'language', parsed.get('language') or podcast.language) podcast.content_types = (','.join(parsed.get('content_types')) or podcast.content_types) # podcast.tags['feed'] = parsed.tags or podcast.tags.get('feed', []) podcast.common_episode_title = to_maxlength( Podcast, 'common_episode_title', parsed.get('common_title') or podcast.common_episode_title, ) podcast.new_location = parsed.get( 'new_location') or podcast.new_location podcast.flattr_url = to_maxlength( Podcast, 'flattr_url', parsed.get('flattr') or podcast.flattr_url) podcast.hub = parsed.get('hub') or podcast.hub podcast.license = parsed.get('license') or podcast.license podcast.max_episode_order = episode_updater.max_episode_order podcast.add_missing_urls(parsed.get('urls', [])) if podcast.new_location: try: new_podcast = Podcast.objects.get( urls__url=podcast.new_location) if new_podcast != podcast: self._mark_outdated(podcast, 'redirected to different podcast', episode_updater) return except Podcast.DoesNotExist: podcast.set_url(podcast.new_location) # latest episode timestamp episodes = Episode.objects.filter( podcast=podcast, released__isnull=False).order_by('released') # Determine update interval # Update interval is based on intervals between episodes podcast.update_interval = episode_updater.get_update_interval(episodes) # factor is increased / decreased depending on whether the latest # update has returned episodes if update_result.episodes_added == 0: # no episodes, incr factor newfactor = podcast.update_interval_factor * 1.2 podcast.update_interval_factor = min(1000, newfactor) # never above 1000 elif update_result.episodes_added > 1: # new episodes, decr factor newfactor = podcast.update_interval_factor / 1.2 podcast.update_interval_factor = max(1, newfactor) # never below 1 latest_episode = episodes.last() if latest_episode: podcast.latest_episode_timestamp = latest_episode.released # podcast.episode_count is not update here on purpose. It is, instead, # continuously updated when creating new episodes in # EpisodeManager.get_or_create_for_url self._update_categories(podcast, prev_latest_episode_timestamp) # try to download the logo and reset logo_url to None on http errors found = CoverArt.save_podcast_logo(podcast.logo_url) if not found: podcast.logo_url = None # check if search index should be considered out of date new_index_fields = get_index_fields(podcast) if list(old_index_fields.items()) != list(new_index_fields.items()): podcast.search_index_uptodate = False # The podcast is always saved (not just when there are changes) because # we need to record the last update logger.info('Saving podcast.') podcast.last_update = datetime.utcnow() podcast.save() try: subscribe_at_hub(podcast) except SubscriptionError as se: logger.warn('subscribing to hub failed: %s', str(se)) self.assign_slug(podcast) episode_updater.assign_missing_episode_slugs() update_related_podcasts.delay(podcast.pk)
from django.conf.urls import url from django.views.generic.base import TemplateView, RedirectView from mygpo.web.logo import CoverArt from . import views urlpatterns = [ url(r'^$', views.home, name='home'), url(r'^logo/(?P<size>\d+)/(?P<prefix>.{3})/(?P<filename>[^/]*)$', CoverArt.as_view(), name='logo'), url(r'^tags/', views.mytags, name='tags'), url(r'^online-help', RedirectView.as_view( url='http://gpoddernet.readthedocs.org/en/latest/user/index.html', permanent=False, ), name='help'), url(r'^developer/', TemplateView.as_view(template_name='developer.html')),
from django.conf.urls import * from django.contrib.auth.views import logout from django.views.generic.base import TemplateView, RedirectView from mygpo.web.logo import CoverArt urlpatterns = patterns('mygpo.web.views', url(r'^$', 'home', name='home'), url(r'^logo/(?P<size>\d+)/(?P<prefix>.{3})/(?P<filename>[^/]*)$', CoverArt.as_view(), name='logo'), url(r'^tags/', 'mytags', name='tags'), url(r'^online-help', RedirectView.as_view( url='http://gpoddernet.readthedocs.org/en/latest/user/index.html', permanent=False, ), name='help'), url(r'^developer/', TemplateView.as_view(template_name='developer.html')), url(r'^contribute/', TemplateView.as_view(template_name='contribute.html'), name='contribute'), url(r'^privacy/', TemplateView.as_view(template_name='privacy_policy.html'), name='privacy-policy'), )
from django.views.generic.base import TemplateView, RedirectView from django.views.static import serve from mygpo.web.logo import CoverArt from . import views urlpatterns = [ path('', views.home, name='home'), path('logo/<int:size>/<str:prefix>/<str:filename>', CoverArt.as_view(), name='logo'), # Media files are also served in production mode. For performance, these # files should be served by a reverse proxy in practice path('%s<path:path>' % settings.MEDIA_URL.lstrip('/'), serve, name='media', kwargs=dict(document_root=settings.MEDIA_ROOT) ), path('tags/', views.mytags, name='tags'), path('online-help',
import re from django.urls import path from django.conf import settings from django.views.generic.base import TemplateView, RedirectView from django.views.static import serve from mygpo.web.logo import CoverArt from . import views urlpatterns = [ path('', views.home, name='home'), path( 'logo/<int:size>/<str:prefix>/<str:filename>', CoverArt.as_view(), name='logo' ), # Media files are also served in production mode. For performance, these # files should be served by a reverse proxy in practice path( '%s<path:path>' % settings.MEDIA_URL.lstrip('/'), serve, name='media', kwargs=dict(document_root=settings.MEDIA_ROOT), ), path('tags/', views.mytags, name='tags'), path( 'online-help', RedirectView.as_view( url='http://gpoddernet.readthedocs.org/en/latest/user/index.html', permanent=False,