diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py index 28485b15da91..ff5ce4ddd1b9 100644 --- a/cms/djangoapps/contentstore/toggles.py +++ b/cms/djangoapps/contentstore/toggles.py @@ -2,8 +2,9 @@ CMS feature toggles. """ from edx_toggles.toggles import SettingDictToggle, WaffleFlag -from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag +from openedx.core.djangoapps.content.search import api as search_api +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # .. toggle_name: FEATURES['ENABLE_EXPORT_GIT'] # .. toggle_implementation: SettingDictToggle @@ -66,30 +67,6 @@ def bypass_olx_failure_enabled(): return BYPASS_OLX_FAILURE.is_enabled() -# .. toggle_name: contentstore.enable_audio_description -# .. toggle_implementation: CourseWaffleFlag -# .. toggle_default: False -# .. toggle_description: Enables the audio description (AD) upload UI in the -# Studio video editor and the corresponding studio_audio_description XBlock -# handler. When disabled, course authors cannot upload, replace, or delete -# audio description files for video blocks. Playback of AD files that were -# already uploaded is NOT gated by this flag and continues to work in the -# LMS -- disable the playback path with a separate flag if needed. -# .. toggle_use_cases: open_edx -# .. toggle_creation_date: 2026-04-07 -ENABLE_AUDIO_DESCRIPTION = CourseWaffleFlag( - f'{CONTENTSTORE_NAMESPACE}.enable_audio_description', - __name__, -) - - -def audio_description_enabled(course_key): - """ - Return True if the audio description upload UI and handler are enabled. - """ - return ENABLE_AUDIO_DESCRIPTION.is_enabled(course_key) - - # .. toggle_name: contentstore.enable_transcript_editor # .. toggle_implementation: CourseWaffleFlag # .. toggle_default: False @@ -660,7 +637,6 @@ def libraries_v2_enabled(): Requires the ENABLE_CONTENT_LIBRARIES feature flag to be enabled, plus Meilisearch. """ - from openedx.core.djangoapps.content.search import api as search_api # pylint: disable=import-outside-toplevel return ( ENABLE_CONTENT_LIBRARIES.is_enabled() and search_api.is_meilisearch_enabled() and diff --git a/openedx/core/djangoapps/video_config/toggles.py b/openedx/core/djangoapps/video_config/toggles.py index 9569a00edd7d..f42c3953d34a 100644 --- a/openedx/core/djangoapps/video_config/toggles.py +++ b/openedx/core/djangoapps/video_config/toggles.py @@ -44,3 +44,26 @@ def use_xpert_translations_component(course_key): Returns a boolean if xpert translations ui component is enabled """ return XPERT_TRANSLATIONS_UI.is_enabled(course_key) + +# .. toggle_name: contentstore.enable_audio_description +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: Enables the audio description (AD) upload UI in the +# Studio video editor and the corresponding studio_audio_description XBlock +# handler. When disabled, course authors cannot upload, replace, or delete +# audio description files for video blocks. Playback of AD files that were +# already uploaded is NOT gated by this flag and continues to work in the +# LMS -- disable the playback path with a separate flag if needed. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2026-04-07 +ENABLE_AUDIO_DESCRIPTION = CourseWaffleFlag( + 'contentstore.enable_audio_description', + __name__, +) + + +def audio_description_enabled(course_key): + """ + Return True if the audio description upload UI and handler are enabled. + """ + return ENABLE_AUDIO_DESCRIPTION.is_enabled(course_key) diff --git a/xmodule/video_block/video_block.py b/xmodule/video_block/video_block.py index e74370b9b1a0..eb67c2d43163 100644 --- a/xmodule/video_block/video_block.py +++ b/xmodule/video_block/video_block.py @@ -32,17 +32,18 @@ from xblocks_contrib.video import VideoBlock as _ExtractedVideoBlock from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE, ATTR_KEY_USER_ID -from openedx.core.djangoapps.video_config.models import HLSPlaybackEnabledFlag, CourseYoutubeBlockedFlag -from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE, TRANSCRIPT_FEEDBACK +from openedx.core.djangoapps.video_config.models import CourseYoutubeBlockedFlag, HLSPlaybackEnabledFlag +from openedx.core.djangoapps.video_config.toggles import ( + PUBLIC_VIDEO_SHARE, + TRANSCRIPT_FEEDBACK, + audio_description_enabled +) from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE from openedx.core.lib.cache_utils import request_cached from openedx.core.lib.courses import get_course_by_id from openedx.core.lib.license import LicenseMixin from xmodule.contentstore.content import StaticContent -from xmodule.course_block import ( - COURSE_VIDEO_SHARING_ALL_VIDEOS, - COURSE_VIDEO_SHARING_NONE, -) +from xmodule.course_block import COURSE_VIDEO_SHARING_ALL_VIDEOS, COURSE_VIDEO_SHARING_NONE from xmodule.editing_block import EditingMixin from xmodule.exceptions import NotFoundError from xmodule.mako_block import MakoTemplateBlockBase @@ -52,11 +53,15 @@ from xmodule.validation import StudioValidation, StudioValidationMessage from xmodule.video_block import manage_video_subtitles_save from xmodule.x_module import ( - PUBLIC_VIEW, STUDENT_VIEW, - ResourceTemplates, shim_xmodule_js, - XModuleMixin, XModuleToXBlockMixin, + PUBLIC_VIEW, + STUDENT_VIEW, + ResourceTemplates, + XModuleMixin, + XModuleToXBlockMixin, + shim_xmodule_js ) from xmodule.xml_block import XmlMixin, deserialize_field, is_pointer_tag, name_to_pathname + from .bumper_utils import bumperize from .sharing_sites import sharing_sites_info_for_video from .transcripts_utils import ( @@ -476,9 +481,6 @@ def get_html(self, view=STUDENT_VIEW, context=None): # lint-amnesty, pylint: di metadata['audioDescriptionUrl'] = audio_description_url try: - from cms.djangoapps.contentstore.toggles import ( # pylint: disable=import-outside-toplevel - audio_description_enabled - ) ad_feature_enabled = bool(audio_description_enabled(self.course_id)) except Exception: # pylint: disable=broad-except ad_feature_enabled = False @@ -991,7 +993,6 @@ def get_youtube_link(video_id): } _context.update({'transcripts_basic_tab_metadata': metadata}) - from cms.djangoapps.contentstore.toggles import audio_description_enabled # pylint: disable=import-outside-toplevel # Audio description upload context for studio editor ad_upload_enabled = bool( diff --git a/xmodule/video_block/video_handlers.py b/xmodule/video_block/video_handlers.py index a7cc8f2f49b2..3545b52c4bd8 100644 --- a/xmodule/video_block/video_handlers.py +++ b/xmodule/video_block/video_handlers.py @@ -14,14 +14,14 @@ from django.utils.timezone import now from edxval.api import create_external_video, create_or_update_video_transcript, delete_video_transcript from opaque_keys.edx.locator import CourseLocator, LibraryLocatorV2 - from webob import Response from xblock.core import XBlock from xblock.exceptions import JsonHandlerError +from openedx.core.djangoapps.content_libraries import api as lib_api +from openedx.core.djangoapps.video_config.toggles import audio_description_enabled from xmodule.exceptions import NotFoundError from xmodule.fields import RelativeTime -from openedx.core.djangoapps.content_libraries import api as lib_api from .transcripts_utils import ( Transcript, @@ -694,9 +694,8 @@ def studio_audio_description(self, request, suffix=''): # pylint: disable=unuse AudioDescriptionUploadError, delete_audio_description, get_audio_description_url, - upload_audio_description, + upload_audio_description ) - from cms.djangoapps.contentstore.toggles import audio_description_enabled # pylint: disable=import-outside-toplevel if not audio_description_enabled or not audio_description_enabled(self.course_id): return Response(status=404)