This repository has been archived by the owner on Feb 13, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into ia-logging
- Loading branch information
Showing
18 changed files
with
431 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "2.6.0" | ||
__version__ = "2.7.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
import djbetty.fields | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='VideohubVideo', | ||
fields=[ | ||
('id', models.IntegerField(serialize=False, primary_key=True)), | ||
('title', models.CharField(max_length=512)), | ||
('description', models.TextField(blank=True, default='')), | ||
('keywords', models.TextField(blank=True, default='')), | ||
('image', djbetty.fields.ImageField(blank=True, null=True, alt_field='_image_alt', default=None, caption_field='_image_caption')), | ||
('_image_alt', models.CharField(blank=True, null=True, max_length=255, editable=False)), | ||
('_image_caption', models.CharField(blank=True, null=True, max_length=255, editable=False)), | ||
('channel_id', models.IntegerField(blank=True, null=True, default=None)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.db import models | ||
|
||
from .models import VideohubVideo | ||
|
||
|
||
class VideoMixin(models.Model): | ||
|
||
"""Provides an OnionStudios (videohub) reference ID, standardized across all properties.""" | ||
|
||
videohub_ref = models.ForeignKey(VideohubVideo, null=True, blank=True) | ||
|
||
class Meta: | ||
abstract = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import json | ||
|
||
from djes.models import Indexable | ||
|
||
from django.conf import settings | ||
from django.db import models | ||
from django.template.defaultfilters import slugify | ||
|
||
from djbetty.fields import ImageField | ||
|
||
import requests | ||
|
||
import six | ||
|
||
|
||
class VideohubVideo(Indexable): | ||
"""A reference to a video on the onion videohub.""" | ||
title = models.CharField(max_length=512) | ||
description = models.TextField(blank=True, default="") | ||
keywords = models.TextField(blank=True, default="") | ||
image = ImageField(null=True, blank=True, alt_field="_image_alt", | ||
caption_field="_image_caption") | ||
_image_alt = models.CharField(null=True, blank=True, editable=False, max_length=255) | ||
_image_caption = models.CharField(null=True, blank=True, editable=False, max_length=255) | ||
# External FK on videohub. Temp workaround until metadata refactor. | ||
channel_id = models.IntegerField(blank=True, null=True, default=None) | ||
|
||
# default values | ||
DEFAULT_VIDEOHUB_VIDEO_URL = "http://videohub.local/videos/{}" | ||
DEFAULT_VIDEOHUB_EMBED_URL = "http://videohub.local/embed?id={}" | ||
DEFAULT_VIDEOHUB_API_URL = "http://videohub.local/api/v0/videos/{}" | ||
DEFAULT_VIDEOHUB_API_SEARCH_URL = "http://videohub.local/api/v0/videos/search" | ||
|
||
class Mapping: | ||
class Meta: | ||
# Exclude image until we actually need it, to avoid dealing with custom mappings | ||
excludes = ('image',) | ||
|
||
@classmethod | ||
def get_serializer_class(cls): | ||
from .serializers import VideohubVideoSerializer | ||
return VideohubVideoSerializer | ||
|
||
@classmethod | ||
def search_videohub(cls, query, filters=None, status=None, sort=None, size=None, page=None): | ||
"""searches the videohub given a query and applies given filters and other bits | ||
:see: https://github.com/theonion/videohub/blob/master/docs/search/post.md | ||
:see: https://github.com/theonion/videohub/blob/master/docs/search/get.md | ||
:param query: query terms to search by | ||
:type query: str | ||
:example query: "brooklyn hipsters" # although, this is a little redundant... | ||
:param filters: video field value restrictions | ||
:type filters: dict | ||
:default filters: None | ||
:example filters: {"channel": "onion"} or {"series": "Today NOW"} | ||
:param status: limit the results to videos that are published, scheduled, draft | ||
:type status: str | ||
:default status: None | ||
:example status: "published" or "draft" or "scheduled" | ||
:param sort: video field related sorting | ||
:type sort: dict | ||
:default sort: None | ||
:example sort: {"title": "desc"} or {"description": "asc"} | ||
:param size: the page size (number of results) | ||
:type size: int | ||
:default size: None | ||
:example size": {"size": 20} | ||
:param page: the page number of the results | ||
:type page: int | ||
:default page: None | ||
:example page: {"page": 2} # note, you should use `size` in conjunction with `page` | ||
:return: a dictionary of results and meta information | ||
:rtype: dict | ||
""" | ||
# construct url | ||
url = getattr(settings, "VIDEOHUB_API_SEARCH_URL", cls.DEFAULT_VIDEOHUB_API_SEARCH_URL) | ||
# construct auth headers | ||
headers = { | ||
"Content-Type": "application/json", | ||
"Authorization": settings.VIDEOHUB_API_TOKEN, | ||
} | ||
# construct payload | ||
payload = { | ||
"query": query, | ||
} | ||
if filters: | ||
assert isinstance(filters, dict) | ||
payload["filters"] = filters | ||
if status: | ||
assert isinstance(status, six.string_types) | ||
payload.setdefault("filters", {}) | ||
payload["filters"]["status"] = status | ||
if sort: | ||
assert isinstance(sort, dict) | ||
payload["sort"] = sort | ||
if size: | ||
assert isinstance(size, (six.string_types, int)) | ||
payload["size"] = size | ||
if page: | ||
assert isinstance(page, (six.string_types, int)) | ||
payload["page"] = page | ||
# send request | ||
res = requests.post(url, data=json.dumps(payload), headers=headers) | ||
# raise if not 200 | ||
if res.status_code != 200: | ||
res.raise_for_status() | ||
# parse and return response | ||
return json.loads(res.content) | ||
|
||
def get_hub_url(self): | ||
"""gets a canonical path to the detail page of the video on the hub | ||
:return: the path to the consumer ui detail page of the video | ||
:rtype: str | ||
""" | ||
url = getattr(settings, "VIDEOHUB_VIDEO_URL", self.DEFAULT_VIDEOHUB_VIDEO_URL) | ||
|
||
# slugify needs ascii | ||
ascii_title = "" | ||
if isinstance(self.title, str): | ||
ascii_title = self.title | ||
elif six.PY2 and isinstance(self.title, six.text_type): | ||
# Legacy unicode conversion | ||
ascii_title = self.title.encode('ascii', 'replace') | ||
|
||
path = slugify("{}-{}".format(ascii_title, self.id)) | ||
|
||
return url.format(path) | ||
|
||
def get_embed_url(self, targeting=None, recirc=None): | ||
"""gets a canonical path to an embedded iframe of the video from the hub | ||
:return: the path to create an embedded iframe of the video | ||
:rtype: str | ||
""" | ||
url = getattr(settings, "VIDEOHUB_EMBED_URL", self.DEFAULT_VIDEOHUB_EMBED_URL) | ||
url = url.format(self.id) | ||
if targeting is not None: | ||
for k, v in sorted(targeting.items()): | ||
url += '&{0}={1}'.format(k, v) | ||
if recirc is not None: | ||
url += '&recirc={0}'.format(recirc) | ||
return url | ||
|
||
def get_api_url(self): | ||
"""gets a canonical path to the api detail url of the video on the hub | ||
:return: the path to the api detail of the video | ||
:rtype: str | ||
""" | ||
url = getattr(settings, 'VIDEOHUB_API_URL', None) | ||
# Support alternate setting (used by most client projects) | ||
if not url: | ||
url = getattr(settings, 'VIDEOHUB_API_BASE_URL', None) | ||
if url: | ||
url = url.rstrip('/') + '/videos/{}' | ||
if not url: | ||
url = self.DEFAULT_VIDEOHUB_API_URL | ||
return url.format(self.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from djbetty.serializers import ImageFieldSerializer | ||
from rest_framework import serializers | ||
|
||
from .models import VideohubVideo | ||
|
||
|
||
class VideohubVideoSerializer(serializers.ModelSerializer): | ||
id = serializers.IntegerField() | ||
image = ImageFieldSerializer(allow_null=True, default={}) | ||
hub_url = serializers.CharField(source="get_hub_url", read_only=True) | ||
embed_url = serializers.CharField(source="get_embed_url", read_only=True) | ||
api_url = serializers.CharField(source="get_api_url", read_only=True) | ||
channel_id = serializers.IntegerField() | ||
|
||
class Meta: | ||
model = VideohubVideo | ||
|
||
def save(self, **kwargs): | ||
""" | ||
Save and return a list of object instances. | ||
""" | ||
validated_data = [ | ||
dict(list(attrs.items()) + list(kwargs.items())) | ||
for attrs in self.validated_data | ||
] | ||
|
||
if "id" in validated_data: | ||
ModelClass = self.Meta.model | ||
|
||
try: | ||
self.instance = ModelClass.objects.get(id=validated_data["id"]) | ||
except ModelClass.DoesNotExist: | ||
pass | ||
|
||
return super(VideohubVideoSerializer, self).save(**kwargs) | ||
|
||
def to_internal_value(self, data): | ||
# Channel info passed as nested object, but we just store integer ID | ||
channel = data.get('channel') | ||
if channel and 'id' in channel: | ||
data['channel_id'] = channel['id'] | ||
return super(VideohubVideoSerializer, self).to_internal_value(data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<div class="hub-video"> | ||
<div class="hub-video-container"> | ||
<iframe name="embedded" allowfullscreen webkitallowfullscreen mozallowfullscreen frameborder="no" width="100%" height="auto" scrolling="no" src="{{ video.get_embed_url }}"></iframe> | ||
</div> | ||
</div> |
27 changes: 27 additions & 0 deletions
27
example/testcontent/migrations/0010_testvideocontentobj.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('content', '0010_content_instant_article_id'), | ||
('videos', '0001_initial'), | ||
('testcontent', '0009_testcontentobjthree'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='TestVideoContentObj', | ||
fields=[ | ||
('content_ptr', models.OneToOneField(auto_created=True, serialize=False, primary_key=True, parent_link=True, to='content.Content')), | ||
('videohub_ref', models.ForeignKey(blank=True, to='videos.VideohubVideo', null=True)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
bases=('content.content', models.Model), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
#!/bin/sh -e | ||
|
||
django-admin makemigrations --settings=example.settings content | ||
django-admin makemigrations --settings=example.settings "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.