Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ coverage*.json
.noseids

# Translations
*.mo
*.pot

# Django stuff:
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [1.1.0] - 2022-01-13

### Feat: Sync with Raccoongang

- Add translations support.
- Remove brightcove.css to fix player control buttons duplication.
- Minor fixes according to relocation to py3.


## [1.0.2] - 2021-08-27

### Fixed

- Fix for BrightCove transcripts which weren't scrolling.
- Autoplay Chrome checks don't throw TypeError if no player.cache_.src property.

## [1.0.1] - 2021-08-24

### Fixed
Expand Down
27 changes: 27 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,30 @@ vendored: $(vendored_js) $(vendored_css) $(vendored_fonts) ## Update vendored J

help:
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)


# Localisation tasks

WORKING_DIR := video_xblock
JS_TARGET := $(WORKING_DIR)/static/js/translations
EXTRACT_DIR := $(WORKING_DIR)/translations/en/LC_MESSAGES
EXTRACTED_DJANGO := $(EXTRACT_DIR)/django-partial.po
EXTRACTED_DJANGOJS := $(EXTRACT_DIR)/djangojs-partial.po
EXTRACTED_TEXT := $(EXTRACT_DIR)/text.po
I18N_CONFIG_PATH = translations/config.yaml


extract_translations: ## extract strings to be translated, outputting .po files
cd $(WORKING_DIR) && i18n_tool extract
mv $(EXTRACTED_DJANGO) $(EXTRACTED_TEXT)
tail -n +20 $(EXTRACTED_DJANGOJS) >> $(EXTRACTED_TEXT)
rm $(EXTRACTED_DJANGOJS)
sed -i'' -e 's/nplurals=INTEGER/nplurals=2/' $(EXTRACTED_TEXT)
sed -i'' -e 's/plural=EXPRESSION/plural=\(n != 1\)/' $(EXTRACTED_TEXT)

compile_translations: ## compile translation files, outputting .mo files for each supported language
cd $(WORKING_DIR) && i18n_tool generate -c $(I18N_CONFIG_PATH)
python manage.py compilejsi18n --output $(JS_TARGET)

dummy_translations: ## generate dummy translation (.po) files
cd $(WORKING_DIR) && i18n_tool dummy
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,30 @@ Run tests:
VideoXBlock is bundled with a set of XBlock-SDK Workbench scenarios.
See [workbench docs](/video_xblock/workbench/README.md) for details.

## Translations

Run docker container to work with translations, tests etc.

```shell
> cd xblock-video
> docker run -it --rm --name video-xblock-tests -v $(pwd):/app seriallab/python3.5dev bash
> cd app
> export VIRTUAL_ENV=$(pwd)
> apt-get install -y gettext
> make deps-test
```

To add new language for translation:

- add appropriate language to the translations.settings.LANGUAGE variable
- create `<lang>`/LC_MESSAGES/ directory and copy there the text.po file from english language
- run:

```shell
> make compile_translations
```


## License

The code in this repository is licensed under the GPL v3 licence unless
Expand Down
11 changes: 11 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os
import sys
from django.core.management import execute_from_command_line

if __name__ == "__main__":
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"video_xblock.translations.settings"
)

execute_from_command_line(sys.argv)
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
le-pycaption==2.2.0a1
requests>=2.9.1,<3.0.0
babelfish>=0.5.5,<0.6.0
XBlock==1.3.1
XBlock==1.4.0
django-statici18n==1.9.0

-e git+https://github.com/edx/[email protected]#egg=edx-i18n-tools
git+https://github.com/edx/[email protected]#egg=xblock-utils==2.1.1
git+https://github.com/edx/[email protected]#egg=xblock-utils==2.1.1
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def package_data(pkg, roots):
'le-pycaption==2.2.0a1',
'requests>=2.9.1,<3.0.0',
'babelfish>=0.5.5,<0.6.0',
'XBlock==1.3.1',
'xblock-utils==2.1.1'
'XBlock>=1.4.0,<2',
'xblock-utils>=2.1,<3'
],
entry_points={
'xblock.v1': [
Expand Down
2 changes: 1 addition & 1 deletion test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ pycodestyle==2.6.0
pydocstyle==5.1.1
selenium>=3.3.3,<4.0

-e git+https://github.com/edx/xblock-sdk.git@0.2.2#egg=xblock-sdk
-e git+https://github.com/edx/xblock-sdk.git@0.3.0#egg=xblock-sdk
2 changes: 1 addition & 1 deletion video_xblock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Video xblock module.
"""

__version__ = '1.0.2'
__version__ = '1.1.0'

# pylint: disable=wildcard-import
from .video_xblock import * # nopep8
9 changes: 5 additions & 4 deletions video_xblock/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from video_xblock.utils import render_resource, render_template, resource_string, ugettext as _


class BaseApiClient(object):
class BaseApiClient:
"""
Low level video platform API client.

Expand Down Expand Up @@ -76,7 +76,7 @@ def __init__(self, xblock):
"""
self.xblock = xblock

@abc.abstractproperty
@abc.abstractmethod
def url_re(self):
"""
Regex (list) to match video url.
Expand All @@ -85,7 +85,8 @@ def url_re(self):
"""
return [] or re.compile('') or ''

@abc.abstractproperty
@property
@abc.abstractmethod
def captions_api(self):
"""
Dictionary of url, request parameters, and response structure of video platform's captions API.
Expand Down Expand Up @@ -310,7 +311,7 @@ def match(cls, href):

`cls.url_re` attribute, defined in subclassess, is used for the check.
"""
if isinstance(cls.url_re, list):
if isinstance(cls.url_re, list): # pylint: disable=no-else-return
return any(regex.search(href) for regex in cls.url_re)
elif isinstance(cls.url_re, type(re.compile(''))):
return cls.url_re.search(href) # pylint: disable=no-member
Expand Down
21 changes: 10 additions & 11 deletions video_xblock/backends/brightcove.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from datetime import datetime
import json
import http.client
import http.client as http_client
import logging
import re

Expand Down Expand Up @@ -80,7 +80,7 @@ def create_credentials(token, account_id):
response = requests.post(url, json=data, headers=headers)
response_data = response.json()
# New resource must have been created.
if response.status_code == http.client.CREATED and response_data:
if response.status_code == http_client.CREATED and response_data:
client_secret = response_data.get('client_secret')
client_id = response_data.get('client_id')
error_message = ''
Expand All @@ -104,7 +104,7 @@ def _refresh_access_token(self):

try:
resp = requests.post(url, auth=basicauth, headers=headers, data=params)
if resp.status_code == http.client.OK:
if resp.status_code == http_client.OK:
result = resp.json()
return result['access_token']
except IOError:
Expand All @@ -126,9 +126,9 @@ def get(self, url, headers=None, can_retry=True):
if headers is not None:
headers_.update(headers)
resp = requests.get(url, headers=headers_)
if resp.status_code == http.client.OK:
if resp.status_code == http_client.OK: # pylint: disable=no-else-return
return resp.json()
elif resp.status_code == http.client.UNAUTHORIZED and can_retry:
elif resp.status_code == http_client.UNAUTHORIZED and can_retry:
self.access_token = self._refresh_access_token()
return self.get(url, headers, can_retry=False)
else:
Expand All @@ -155,9 +155,9 @@ def post(self, url, payload, headers=None, can_retry=True):

resp = requests.post(url, data=payload, headers=headers_)
log.debug("BC response status: {}".format(resp.status_code))
if resp.status_code in (http.client.OK, http.client.CREATED):
if resp.status_code in (http_client.OK, http_client.CREATED): # pylint: disable=no-else-return
return resp.json()
elif resp.status_code == http.client.UNAUTHORIZED and can_retry:
elif resp.status_code == http_client.UNAUTHORIZED and can_retry:
self.access_token = self._refresh_access_token()
return self.post(url, payload, headers, can_retry=False)

Expand All @@ -171,7 +171,7 @@ def post(self, url, payload, headers=None, can_retry=True):
return resp_dict


class BrightcoveHlsMixin(object):
class BrightcoveHlsMixin:
"""
Encapsulate data and methods used for HLS specific features.

Expand Down Expand Up @@ -609,6 +609,5 @@ def download_default_transcript(self, url=None, language_code=None): # pylint:
if url is None:
raise VideoXBlockException(_('`url` parameter is required.'))
data = requests.get(url)
text = data.content
cleaned_captions_text = remove_escaping(text)
return cleaned_captions_text

return remove_escaping(data.content)
1 change: 1 addition & 0 deletions video_xblock/backends/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def media_id(self, href): # pylint: disable=unused-argument
"""
return '<media_id>'

@property
def captions_api(self):
"""
Dictionary of url, request parameters, and response structure of video platform's captions API.
Expand Down
9 changes: 4 additions & 5 deletions video_xblock/backends/vimeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Vimeo Video player plugin.
"""

import http.client
import http.client as http_client
import json
import logging
import re
Expand Down Expand Up @@ -57,7 +57,7 @@ def get(self, url, headers=None, can_retry=False):
if headers is not None:
headers_.update(headers)
resp = requests.get(url, headers=headers_)
if resp.status_code == http.client.OK:
if resp.status_code == http_client.OK:
return resp.json()
else:
raise VimeoApiClientError(_("Can't fetch requested data from API."))
Expand Down Expand Up @@ -275,6 +275,5 @@ def download_default_transcript(self, url, language_code=None): # pylint: disab
sub (str): Transcripts formatted per WebVTT format https://w3c.github.io/webvtt/
"""
data = requests.get(url)
text = data.content
cleaned_captions_text = remove_escaping(text)
return cleaned_captions_text

return remove_escaping(data.content)
6 changes: 3 additions & 3 deletions video_xblock/backends/wistia.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from html import parser as html_parser
import json
import http.client as httplib
import http.client as http_client
import logging
import re

Expand Down Expand Up @@ -146,7 +146,7 @@ def authenticate_api(self, **kwargs):
auth_data['token'] = token
url = self.captions_api.get('auth_sample_url').format(token=str(token))
response = requests.get('https://' + url)
if response.status_code == httplib.UNAUTHORIZED:
if response.status_code == http_client.UNAUTHORIZED:
error_message = "Authentication failed. " \
"Please ensure you have provided a valid master token, using Video API Token field."
return auth_data, error_message
Expand Down Expand Up @@ -194,7 +194,7 @@ def get_default_transcripts(self, **kwargs):

# If a video does not exist, the response will be an empty HTTP 404 Not Found.
# Reference: https://wistia.com/doc/data-api#captions_index
if response.status_code == httplib.NOT_FOUND:
if response.status_code == http_client.NOT_FOUND:
message = _("Wistia video {} doesn't exist.").format(video_id)
return self.default_transcripts, message

Expand Down
5 changes: 2 additions & 3 deletions video_xblock/backends/youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from html.parser import unescape, HTMLParser
import json
import http.client as httplib
import http.client as http_client
import re
import textwrap
import urllib.request, urllib.parse, urllib.error
Expand Down Expand Up @@ -124,7 +124,7 @@ def fetch_default_transcripts_languages(self, video_id):
'Error: {}'.format(str(exception))
return available_languages, message

if data.status_code == httplib.OK and data.text:
if data.status_code == http_client.OK and data.text:
youtube_data = etree.fromstring(data.content, parser=utf8_parser)
empty_subs = False if [el.get('transcript_list') for el in youtube_data] else True
available_languages = [
Expand Down Expand Up @@ -241,4 +241,3 @@ def dispatch(self, request, suffix):
"""
Youtube dispatch method.
"""
pass
8 changes: 4 additions & 4 deletions video_xblock/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Status(Enum):
warning = 'warning'


class PlayerName(object):
class PlayerName:
"""
Contains Player names for each backends.
"""
Expand All @@ -29,7 +29,7 @@ class PlayerName(object):
YOUTUBE = 'youtube-player'


class TranscriptSource(object):
class TranscriptSource:
"""
Define transcript source, e.g. where transcript was fetched from.
"""
Expand All @@ -48,7 +48,7 @@ def to_dict(cls):
return {k: getattr(cls, k) for k in cls.ALL}


class TPMApiTranscriptFormatID(object):
class TPMApiTranscriptFormatID:
"""
3PlayMedia service's transcripts API format_name - format_ID mapping.

Expand All @@ -61,7 +61,7 @@ class TPMApiTranscriptFormatID(object):
WEBVTT = 51


class TPMApiLanguage(object):
class TPMApiLanguage:
"""
3PlayMedia service's transcripts API language_ID - language info (e.g.: code, name...) mapping.

Expand Down
7 changes: 7 additions & 0 deletions video_xblock/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def __str__(self):
"""
return self.detail

@property
def message(self):
"""
Backward compatibility with python2.7 error messages.
"""
return self.detail


class VideoXBlockMockException(VideoXBlockException):
"""
Expand Down
1 change: 1 addition & 0 deletions video_xblock/locale
Loading