Skip to content
Merged
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
14 changes: 10 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,26 @@ docs = [
"sphinx-book-theme",
]

# XBlock entry points will be added here as xblocks are migrated
# Example:
# [project.entry-points."xblock.v1"]
# foo = "foo_xblock:FooXBlock"
[project.entry-points."xblock.v1"]
audio = "audio:AudioXBlock"

# Packages live in src/ but are installed without the src prefix
# e.g., src/foo_xblock/ is installed as foo_xblock
[tool.setuptools]
package-dir = {"" = "src"}
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]
exclude = ["tests*"]

[tool.setuptools.package-data]
"*" = [
"static/**/*",
"templates/**/*",
"translations/**/*",
]

# Ruff configuration
# https://docs.astral.sh/ruff/configuration/
[tool.ruff]
Expand Down
9 changes: 9 additions & 0 deletions src/audio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
AudioXBlock
===========

This is a simple XBlock which will play audio files as an HTML5 audio
element. If unavailable, it will fall back to an embed element.

Usage:

<audio src="http://server.tld/static/song.mp3" />
1 change: 1 addition & 0 deletions src/audio/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .audio import AudioXBlock as AudioXBlock
98 changes: 98 additions & 0 deletions src/audio/audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""TO-DO: This XBlock will play an MP3 file as an HTML5 audio element."""

from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblock.utils.resources import ResourceLoader

resource_loader = ResourceLoader(__name__)


def _(text):
"""
Make `_` a no-op, so we can scrape strings
"""
return text


class AudioXBlock(XBlock):
"""
This XBlock will play an MP3 file as an HTML5 audio element.
"""

# Fields are defined on the class. You can access them in your code as
# self.<fieldname>.
src = String(
scope=Scope.settings,
help=_("URL for MP3 file to play"),
)

@staticmethod
def resource_string(path):
"""Handy helper for getting resources from our kit."""
return resource_loader.load_unicode(path)

# TO-DO: change this view to display your data your own way.
def student_view(self, context=None):
"""
The primary view of the AudioXBlock, shown to students
when viewing courses.
"""
frag = Fragment()
frag.add_content(
resource_loader.render_django_template(
"templates/html/audio.html",
context={
"src": self.src,
},
i18n_service=self.runtime.service(self, "i18n"),
)
)
frag.add_css(self.resource_string("static/css/audio.css"))
return frag

def studio_view(self, context):
"""
The view for editing the AudioXBlock parameters inside Studio.
"""
frag = Fragment()
frag.add_content(
resource_loader.render_django_template(
"templates/html/audio_edit.html",
context={
"src": self.src,
},
i18n_service=self.runtime.service(self, "i18n"),
)
)
js = self.resource_string("static/js/src/audio_edit.js")
frag.add_javascript(js)
frag.initialize_js("AudioEditBlock")

return frag

@XBlock.json_handler
def studio_submit(self, data, suffix=""):
"""
Called when submitting the form in Studio.
"""
self.src = data.get("src")

return {"result": "success"}

# TO-DO: change this to create the scenarios you'd like to see in the
# workbench while developing your XBlock.
@staticmethod
def workbench_scenarios():
"""A canned scenario for display in the workbench."""
return [
(
"AudioXBlock",
"""<vertical_demo>
<audio src="http://localhost/Ikea.mp3"> </audio>
<audio src="http://localhost/skull.mp3"> </audio>
<audio src="http://localhost/monkey.mp3"> </audio>
</vertical_demo>
""",
),
]
4 changes: 4 additions & 0 deletions src/audio/conf/locale/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Configuration for i18n workflow.

locales:
- en # English - Source Language
9 changes: 9 additions & 0 deletions src/audio/static/css/audio.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* CSS for AudioXBlock */

.audio_block .count {
font-weight: bold;
}

.audio_block p {
cursor: pointer;
}
16 changes: 16 additions & 0 deletions src/audio/static/js/src/audio_edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function AudioEditBlock(runtime, element) {
$(element).find('.save-button').bind('click', function () {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit');
var data = {
src: $(element).find('input[name=audio_src]').val()
};
$.post(handlerUrl, JSON.stringify(data)).done(function (response) {
runtime.notify('save', { state: 'end' })
window.location.reload(false);
});
});

$(element).find('.cancel-button').bind('click', function () {
runtime.notify('cancel', {});
});
}
7 changes: 7 additions & 0 deletions src/audio/templates/html/audio.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="audio_block">
<audio controls preload>
<source src="{{ src }}">
<embed height="50" width="100" src="{{ src }}">
</audio>

</div>
21 changes: 21 additions & 0 deletions src/audio/templates/html/audio_edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% load i18n %}

<div class="wrapper-comp-settings is-active editor-with-buttons"
id="settings-tab">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="audio_src">{% trans "Audio Source Location" %}</label>
<input class="input setting-input"
name="audio_src"
id="audio_src"
value="{{ src }}"
type="text" />
</div>
</li>
</ul>
<div class="row module-actions">
<a href="#" class="save-button action-primary action">{% trans "Save" %}</a>
<a href="#" class="cancel-button action-secondary action">{% trans "Cancel" %}</a>
</div>
</div>
1 change: 1 addition & 0 deletions src/audio/translations
Loading