def _post_thumbnails(blogitem): blogfiles = BlogFile.objects.filter(blogitem=blogitem).order_by("add_date") images = [] for blogfile in blogfiles: if not os.path.isfile(blogfile.file.path): continue full_im = thumbnail(blogfile.file, "2000x2000", upscale=False, quality=100) full_url = full_im.url image = {"full_url": full_url, "full_size": full_im.size} formats = ( ("small", "120x120"), ("big", "230x230"), ("bigger", "370x370"), # iPhone 6 is 375 ) for key, geometry in formats: im = thumbnail(blogfile.file, geometry, quality=81) url_ = im.url image[key] = { "url": url_, "alt": getattr(blogfile, "title", blogitem.title), "width": im.width, "height": im.height, } images.append(image) return images
def _post_thumbnails(blogitem): blogfiles = BlogFile.objects.filter(blogitem=blogitem).order_by("add_date") images = [] for blogfile in blogfiles: if not os.path.isfile(blogfile.file.path): continue full_im = thumbnail(blogfile.file, "1000x1000", upscale=False, quality=100) full_url = full_im.url image = {"full_url": full_url, "full_size": full_im.size} formats = ( ("small", "120x120"), ("big", "230x230"), ("bigger", "370x370"), # iPhone 6 is 375 ) for key, geometry in formats: im = thumbnail(blogfile.file, geometry, quality=81) url_ = im.url image[key] = { "url": url_, "alt": getattr(blogfile, "title", blogitem.title), "width": im.width, "height": im.height, } images.append(image) return images
def podcast(request, id, slug=None): podcast = get_object_or_404(Podcast, id=id) context = {} context['podcast'] = podcast context['page_title'] = podcast.name episodes = Episode.objects.filter( podcast=podcast ).order_by('-published') if podcast.image and is_html_document(podcast.image.path): print "Found a podcast.image that wasn't an image" podcast.image = None podcast.save() redownload_podcast_image.delay(podcast.id) elif not podcast.image and podcast.image_url: redownload_podcast_image.delay(podcast.id) if podcast.itunes_lookup is None: fetch_itunes_lookup.delay(podcast.id) if not episodes.exists(): download_episodes_task.delay(podcast.id) context['episodes'] = episodes try: context['thumb'] = thumbnail(podcast.image, '300x300') except IOError: # image is so busted it can't be turned into a thumbnail podcast.image = None podcast.save() context['thumb'] = None redownload_podcast_image.delay(podcast.id) return render(request, 'podcasttime/podcast.html', context)
def redownload_podcast_image(podcast_id): podcast = Podcast.objects.get(id=podcast_id) try: podcast.download_image() # If it worked, it should be possible to make a thumbnail out of # if. I've seen downloaded images with the right content-type, # and with a size but when you try to turn it into a thumbnail # PIL throws IOErrors. assert podcast.image try: thumbnail(podcast.image, "300x300") print("Worked!") except IOError: print("Not a valid image if thumbnails can't be made") podcast.image = None podcast.save() except Exception: print("Failed!") PodcastError.create(podcast) raise
def post_thumbnails(request, oid): blogitem = get_object_or_404(BlogItem, oid=oid) blogfiles = ( BlogFile.objects .filter(blogitem=blogitem) .order_by('add_date') ) images = [] for blogfile in blogfiles: full_im = thumbnail( blogfile.file, '1000x1000', upscale=False, quality=100 ) full_url = full_im.url delete_url = reverse('delete_post_thumbnail', args=(blogfile.pk,)) image = { 'full_url': full_url, 'delete_url': delete_url, } for key, geometry in (('small', '120x120'), ('big', '230x230')): im = thumbnail( blogfile.file, geometry, quality=81 ) url_ = im.url image[key] = { 'url': url_, 'alt': getattr(blogfile, 'title', blogitem.title), 'width': im.width, 'height': im.height, } images.append(image) return http.JsonResponse({'images': images})
def picks_data(request): context = {} qs = Picked.objects.all().order_by('-modified') paginator = Paginator(qs, 5) # XXX make this something bigger like 15 page = request.GET.get('page') try: paged = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. paged = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. paged = paginator.page(paginator.num_pages) items = [] # XXX ALL of this needs to be optimized for pick in paged: podcasts = [] for podcast in pick.podcasts.all().order_by('-times_picked'): podcasts.append({ 'name': podcast.name, 'image': ( podcast.image and thumbnail(podcast.image, '300x300').url or None ), 'times_picked': podcast.times_picked, 'id': podcast.id, 'slug': podcast.get_or_create_slug(), }) items.append({'podcasts': podcasts, 'id': pick.id}) context['items'] = items pagination = { 'has_previous': paged.has_previous(), 'has_next': paged.has_next(), 'number': paged.number, 'num_pages': paginator.num_pages, } if pagination['has_previous']: pagination['previous_page_number'] = paged.previous_page_number() if pagination['has_next']: pagination['next_page_number'] = paged.next_page_number() context['pagination'] = pagination return http.JsonResponse(context)
def _render_blog_post(request, oid, screenshot_mode=False): if oid.endswith("/"): oid = oid[:-1] try: post = BlogItem.objects.get(oid=oid) except BlogItem.DoesNotExist: try: post = BlogItem.objects.get(oid__iexact=oid) except BlogItem.DoesNotExist: if oid == "add": return redirect(reverse("add_post")) raise http.Http404(oid) # If you try to view a blog post that is beyond one day in the # the future it should raise a 404 error. future = timezone.now() + datetime.timedelta(days=1) if post.pub_date > future: raise http.Http404("not published yet") # Reasons for not being here if request.method == "HEAD": return http.HttpResponse("") elif request.method == "GET" and ( request.GET.get("replypath") or request.GET.get("show-comments") ): return http.HttpResponsePermanentRedirect(request.path) # attach a field called `_absolute_url` which depends on the request base_url = "https://" if request.is_secure() else "http://" base_url += RequestSite(request).domain post._absolute_url = base_url + reverse("blog_post", args=(post.oid,)) context = {"post": post, "screenshot_mode": screenshot_mode} if request.path != "/plog/blogitem-040601-1": try: context["previous_post"] = post.get_previous_by_pub_date() except BlogItem.DoesNotExist: context["previous_post"] = None try: context["next_post"] = post.get_next_by_pub_date(pub_date__lt=utc_now()) except BlogItem.DoesNotExist: context["next_post"] = None if post.screenshot_image: context["screenshot_image"] = thumbnail( post.screenshot_image, "1280x1000", quality=90 ).url if context["screenshot_image"].startswith("//"): # facebook is not going to like that context["screenshot_image"] = "https:" + context["screenshot_image"] else: context["screenshot_image"] = None # Cheat a little and make the open graph image absolute if need be. if post.open_graph_image and "://" not in post.open_graph_image: post.open_graph_image = request.build_absolute_uri(post.open_graph_image) comments = ( BlogComment.objects.filter(blogitem=post, approved=True) .order_by("add_date") .only( "oid", "blogitem_id", "parent_id", "approved", "comment_rendered", "add_date", "name", ) ) comments_truncated = False count_comments = post.count_comments() if request.GET.get("comments") != "all": slice_m, slice_n = ( max(0, count_comments - settings.MAX_RECENT_COMMENTS), count_comments, ) if count_comments > settings.MAX_RECENT_COMMENTS: comments_truncated = settings.MAX_RECENT_COMMENTS comments = comments = comments[slice_m:slice_n] all_comments = defaultdict(list) for comment in comments: all_comments[comment.parent_id].append(comment) # print(all_comments.keys()) context["comments_truncated"] = comments_truncated context["count_comments"] = count_comments context["all_comments"] = all_comments if request.path != "/plog/blogitem-040601-1": context["related_by_keyword"] = get_related_posts_by_keyword(post, limit=5) # context["related_by_text"] = get_related_posts_by_text(post, limit=5) context["show_buttons"] = not screenshot_mode context["show_carbon_ad"] = not screenshot_mode context["home_url"] = request.build_absolute_uri("/") context["page_title"] = post.title context["pub_date_years"] = THIS_YEAR - post.pub_date.year return render(request, "plog/post.html", context)
def _render_blog_post(request, oid, page=None, screenshot_mode=False): if oid.endswith("/"): oid = oid[:-1] try: post = BlogItem.objects.get(oid=oid) except BlogItem.DoesNotExist: try: post = BlogItem.objects.get(oid__iexact=oid) except BlogItem.DoesNotExist: if oid == "add": return redirect(reverse("add_post")) raise http.Http404(oid) # If you try to view a blog post that is beyond 10 days in the # the future it should raise a 404 error. future = timezone.now() + datetime.timedelta(days=10) if post.pub_date > future: raise http.Http404("not published yet") if page is None: page = 1 else: page = int(page) if page == 1: return redirect("blog_post", oid) if page > settings.MAX_BLOGCOMMENT_PAGES: raise http.Http404("Gone too far") # Reasons for not being here if request.method == "HEAD": return http.HttpResponse("") elif request.method == "GET" and (request.GET.get("replypath") or request.GET.get("show-comments")): return http.HttpResponsePermanentRedirect(request.path) # attach a field called `_absolute_url` which depends on the request base_url = get_base_url(request) post._absolute_url = base_url + reverse("blog_post", args=(post.oid, )) context = {"post": post, "screenshot_mode": screenshot_mode} if "/plog/blogitem-040601-1" not in request.path: try: context["previous_post"] = post.get_previous_by_pub_date() except BlogItem.DoesNotExist: context["previous_post"] = None try: context["next_post"] = post.get_next_by_pub_date( pub_date__lt=timezone.now()) except BlogItem.DoesNotExist: context["next_post"] = None if post.screenshot_image: context["screenshot_image"] = thumbnail(post.screenshot_image, "1280x1000", quality=90).url if context["screenshot_image"].startswith("//"): # facebook is not going to like that context[ "screenshot_image"] = "https:" + context["screenshot_image"] else: context["screenshot_image"] = None # Cheat a little and make the open graph image absolute if need be. if post.open_graph_image and "://" not in post.open_graph_image: post.open_graph_image = request.build_absolute_uri( post.open_graph_image) blogcomments = BlogComment.objects.filter(blogitem=post, approved=True) only = ( "oid", "blogitem_id", "parent_id", "approved", "comment_rendered", "add_date", "name", ) root_comments = (blogcomments.filter( parent__isnull=True).order_by("add_date").only(*only)) replies = blogcomments.filter( parent__isnull=False).order_by("add_date").only(*only) count_comments = blogcomments.count() root_comments_count = root_comments.count() if page > 1: if (page - 1) * settings.MAX_RECENT_COMMENTS > root_comments_count: raise http.Http404("Gone too far") slice_m, slice_n = get_blogcomment_slice(root_comments_count, page) root_comments = root_comments[slice_m:slice_n] comments_truncated = False if root_comments_count > settings.MAX_RECENT_COMMENTS: comments_truncated = settings.MAX_RECENT_COMMENTS all_comments = defaultdict(list) for comment in root_comments: all_comments[comment.parent_id].append(comment) for comment in replies: all_comments[comment.parent_id].append(comment) context["comments_truncated"] = comments_truncated context["count_comments"] = count_comments context["all_comments"] = all_comments if "/plog/blogitem-040601-1" not in request.path: context["related_by_keyword"] = get_related_posts_by_keyword(post, limit=5) context["show_buttons"] = not screenshot_mode context["show_carbon_ad"] = not screenshot_mode # context["show_carbon_ad"] = 0 # context["show_carbon_native_ad"] = context["show_carbon_ad"] # Disabled as of Aug 2019 because the $$$ profit was too small and not # worth the web perf "drag" that it costs. context["show_carbon_native_ad"] = False context["home_url"] = request.build_absolute_uri("/") context["page_title"] = post.title context["pub_date_years"] = THIS_YEAR - post.pub_date.year context["page"] = page if page < settings.MAX_BLOGCOMMENT_PAGES: # But is there even a next page?! if page * settings.MAX_RECENT_COMMENTS < root_comments_count: context["paginate_uri_next"] = reverse("blog_post", args=(post.oid, page + 1)) if page > 1: context["paginate_uri_previous"] = reverse("blog_post", args=(post.oid, page - 1)) # The `post.open_graph_image` is a string. It looks something like this: # '/cache/1e/a7/1ea7b1a42e9161.png' and it would get rendered # into the template like this: # <meta property="og:image" content="/cache/1e/a7/1ea7b1a42e9161.png"> # But post-processing will make this an absolute URL. And that might # not pick up the smarts that `get_base_url(request)` can do so # turn this into a control template context variable. absolute_open_graph_image = None if post.open_graph_image: absolute_open_graph_image = base_url + urlparse( post.open_graph_image).path context["absolute_open_graph_image"] = absolute_open_graph_image context["not_published_yet"] = post.pub_date > timezone.now() response = render(request, "plog/post.html", context) response["x-server"] = "django" # If it hasn't been published yet, don't cache-control it. if context["not_published_yet"]: add_never_cache_headers(response) return response
def get_thumbnail(self, *args, **kwargs): assert self.image, "podcast must have an image" return thumbnail(self.image, *args, **kwargs)
def _render_blog_post(request, oid, screenshot_mode=False): if oid.endswith('/'): oid = oid[:-1] try: post = BlogItem.objects.get(oid=oid) except BlogItem.DoesNotExist: try: post = BlogItem.objects.get(oid__iexact=oid) except BlogItem.DoesNotExist: if oid == 'add': return redirect(reverse('add_post')) raise http.Http404(oid) # If you try to view a blog post that is beyond one day in the # the future it should raise a 404 error. future = timezone.now() + datetime.timedelta(days=1) if post.pub_date > future: raise http.Http404('not published yet') # Reasons for not being here if request.method == 'HEAD': return http.HttpResponse('') elif ( request.method == 'GET' and (request.GET.get('replypath') or request.GET.get('show-comments')) ): return http.HttpResponsePermanentRedirect(request.path) # attach a field called `_absolute_url` which depends on the request base_url = 'https://' if request.is_secure() else 'http://' base_url += RequestSite(request).domain post._absolute_url = base_url + reverse('blog_post', args=(post.oid,)) context = { 'post': post, 'screenshot_mode': screenshot_mode, } try: context['previous_post'] = post.get_previous_by_pub_date() except BlogItem.DoesNotExist: context['previous_post'] = None try: context['next_post'] = post.get_next_by_pub_date( pub_date__lt=utc_now() ) except BlogItem.DoesNotExist: context['next_post'] = None if post.screenshot_image: context['screenshot_image'] = thumbnail( post.screenshot_image, '1280x1000', quality=90 ).url if context['screenshot_image'].startswith('//'): # facebook is not going to like that context['screenshot_image'] = ( 'https:' + context['screenshot_image'] ) else: context['screenshot_image'] = None comments = ( BlogComment.objects .filter(blogitem=post) .order_by('add_date') ) if not request.user.is_staff: comments = comments.filter(approved=True) comments_truncated = False if request.GET.get('comments') != 'all': comments = comments[:100] if post.count_comments() > 100: comments_truncated = 100 all_comments = defaultdict(list) for comment in comments: all_comments[comment.parent_id].append(comment) context['comments_truncated'] = comments_truncated context['all_comments'] = all_comments context['related'] = get_related_posts(post) context['show_buttons'] = ( not screenshot_mode and not settings.DEBUG and request.path != '/plog/blogitem-040601-1' ) context['show_fusion_ad'] = ( not screenshot_mode and not settings.DEBUG ) context['home_url'] = request.build_absolute_uri('/') context['page_title'] = post.title return render(request, 'plog/post.html', context)
def podcast_data(request, id, slug=None): podcast = get_object_or_404(Podcast, id=id, slug__iexact=slug) context = {} context.update({ 'id': podcast.id, 'slug': podcast.slug, 'name': podcast.name, 'url': podcast.url, 'image_url': podcast.image_url, 'times_picked': podcast.times_picked, 'total_seconds': podcast.total_seconds, 'last_fetch': podcast.last_fetch, 'modified': podcast.modified, }) if podcast.error: context['_has_error'] = True if ( not podcast.last_fetch or podcast.last_fetch < timezone.now() - datetime.timedelta(days=7) ): cache_key = 'updating:episodes:{}'.format(podcast.id) if not cache.get(cache_key): cache.set(cache_key, True, 60) download_episodes_task.delay(podcast.id) context['_updating'] = True episodes = Episode.objects.filter( podcast=podcast ).order_by('-published') if podcast.image and is_html_document(podcast.image.path): print "Found a podcast.image that wasn't an image" podcast.image = None podcast.save() redownload_podcast_image.delay(podcast.id) elif not podcast.image and podcast.image_url: redownload_podcast_image.delay(podcast.id) if podcast.itunes_lookup is None: fetch_itunes_lookup.delay(podcast.id) if not episodes.exists(): download_episodes_task.delay(podcast.id) context['episodes_count'] = episodes.count() context['episodes'] = [] for episode in episodes: context['episodes'].append({ 'duration': episode.duration, 'published': episode.published, 'guid': episode.guid, }) try: thumb = thumbnail(podcast.image, '300x300') context['thumb'] = { 'url': thumb.url, 'width': thumb.width, 'height': thumb.height, } except IOError: # image is so busted it can't be turned into a thumbnail podcast.image = None podcast.save() context['thumb'] = None redownload_podcast_image.delay(podcast.id) return http.JsonResponse(context)
def find(request): if not (request.GET.get('ids') or request.GET.get('q')): return http.HttpResponseBadRequest('no ids or q') found = [] max_ = 5 q = None if request.GET.get('ids'): ids = [int(x) for x in request.GET['ids'].split(',')] found = Podcast.objects.filter(id__in=ids) # rearrange them in the order they were found = sorted(found, key=lambda x: ids.index(x.id)) # for podcast in found: # if not podcast.last_fetch: # download_episodes_task.delay(podcast.id) elif request.GET.get('itunes'): q = request.GET['q'] try: results = itunes_search( q, attribute='titleTerm', timeout=6, )['results'] except (ReadTimeout, ConnectTimeout): results = [] for result in results: # pod = { # 'image_url': result['artworkUrl600'], # 'itunes_url': result['collectionViewUrl'], # 'artist_name': result['artistName'], # 'tags': result['genres'], # 'name': result['collectionName'], # # 'feed_url': result['feedUrl'], # } try: podcast = Podcast.objects.get( url=result['feedUrl'], name=result['collectionName'] ) except Podcast.DoesNotExist: podcast = Podcast.objects.create( name=result['collectionName'], url=result['feedUrl'], itunes_lookup=result, image_url=result['artworkUrl600'], ) try: podcast.download_image(timeout=3) except (ReadTimeout, ConnectTimeout): redownload_podcast_image(podcast.id) download_episodes_task.delay(podcast.id) # Reload since the task functions operate on a new instance # podcast = Podcast.objects.get(id=podcast.id) found.append(podcast) else: q = request.GET['q'] items = [] # import time # time.sleep(random.randint(1,4)) base_qs = Podcast.objects.filter(error__isnull=True) podcasts = base_qs.filter(name__istartswith=q) for podcast in podcasts[:max_]: found.append(podcast) if len(q) > 2: sql = ( "to_tsvector('english', name) @@ " "plainto_tsquery('english', %s)" ) podcasts = base_qs.exclude( id__in=[x.id for x in found] ).extra( where=[sql], params=[q] )[:max_] for podcast in podcasts[:max_]: if len(found) >= max_: break found.append(podcast) if len(q) > 1: podcasts = base_qs.filter(name__icontains=q).exclude( id__in=[x.id for x in found] ) for podcast in podcasts[:max_]: if len(found) >= max_: break found.append(podcast) def episodes_meta(podcast): episodes_cache_key = 'episodes-meta%s' % podcast.id meta = cache.get(episodes_cache_key) if meta is None: episodes = Episode.objects.filter(podcast=podcast) episodes_count = episodes.count() total_hours = None if episodes_count: total_seconds = episodes.aggregate( Sum('duration') )['duration__sum'] if total_seconds: total_hours = total_seconds / 3600.0 else: download_episodes_task.delay(podcast.id) meta = { 'count': episodes_count, 'total_hours': total_hours, } if episodes_count: cache.set(episodes_cache_key, meta, 60 * 60 * 24) return meta items = [] for podcast in found: if podcast.image and is_html_document(podcast.image.path): print "Found a podcast.image that wasn't an image" podcast.image = None podcast.save() if podcast.image: if podcast.image.size < 1000: print "IMAGE LOOKS SUSPICIOUS" print podcast.image_url print repr(podcast), podcast.id print podcast.url print repr(podcast.image.read()) podcast.download_image() thumb_url = None if podcast.image: try: thumb_url = thumbnail( podcast.image, '100x100', quality=81, upscale=False ).url thumb_url = make_absolute_url(thumb_url, request) except IOError: import sys print "BAD IMAGE!" print sys.exc_info() print repr(podcast.image) print repr(podcast), podcast.url print podcast.image = None podcast.save() redownload_podcast_image.delay(podcast.id) else: redownload_podcast_image.delay(podcast.id) # Temporarily put here if podcast.itunes_lookup is None: fetch_itunes_lookup.delay(podcast.id) meta = episodes_meta(podcast) episodes_count = meta['count'] total_hours = meta['total_hours'] items.append({ 'id': podcast.id, 'name': podcast.name, 'image_url': thumb_url, 'episodes': episodes_count, 'hours': total_hours, 'last_fetch': podcast.last_fetch, 'slug': podcast.get_or_create_slug(), 'url': reverse( 'podcasttime:podcast_slug', args=(podcast.id, podcast.get_or_create_slug()) ), }) return http.JsonResponse({ 'items': items, 'q': q, })
def podcasts_data(request): context = {} search = request.GET.get('search', '').strip() ids = request.GET.get('ids') podcasts = Podcast.objects.all() if search: podcasts = _search_podcasts(search, podcasts) if ids: ids = [int(x) for x in ids.split(',') if x.strip()] podcasts = podcasts.filter(id__in=ids) podcasts = podcasts.order_by('-times_picked', 'name') paginator = Paginator(podcasts, 15) page = request.GET.get('page') try: paged = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. paged = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. paged = paginator.page(paginator.num_pages) context['count'] = paginator.count # past = timezone.now() - datetime.timedelta(days=365) episodes = Episode.objects.filter( podcast__in=paged, # published__gte=past, ).values( 'podcast_id', ).annotate( duration=Sum('duration'), count=Count('podcast_id'), ) episode_counts = {} episode_seconds = {} for x in episodes: episode_counts[x['podcast_id']] = x['count'] episode_seconds[x['podcast_id']] = x['duration'] items = [] for podcast in paged: item = { 'id': podcast.id, 'name': podcast.name, 'image': ( podcast.image and thumbnail(podcast.image, '300x300').url or None ), 'times_picked': podcast.times_picked, 'slug': podcast.get_or_create_slug(), 'last_fetch': ( podcast.last_fetch and podcast.last_fetch.isoformat() or None ), 'modified': podcast.modified.isoformat(), 'episode_count': episode_counts.get(podcast.id, 0), 'episode_seconds': episode_seconds.get(podcast.id, 0), } items.append(item) context['items'] = items pagination = { 'has_previous': paged.has_previous(), 'has_next': paged.has_next(), 'number': paged.number, 'num_pages': paginator.num_pages, } if pagination['has_previous']: pagination['previous_page_number'] = paged.previous_page_number() if pagination['has_next']: pagination['next_page_number'] = paged.next_page_number() context['pagination'] = pagination return http.JsonResponse(context)