Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
M
Moodle Filter Opencast
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
elc
Moodle Filter Opencast
Commits
a80464e1
Commit
a80464e1
authored
1 year ago
by
Justus Dieckmann
Browse files
Options
Downloads
Patches
Plain Diff
Rewrite filter to also accept <a href="..."> and <video src="...">, for example
parent
48c2bf80
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
filter.php
+194
-111
194 additions, 111 deletions
filter.php
with
194 additions
and
111 deletions
filter.php
+
194
−
111
View file @
a80464e1
...
...
@@ -20,39 +20,68 @@
* This filter will replace any links to opencast videos with the selected player from opencast.
*
* @package filter_opencast
* @copyright 20
18 Tamara Gunkel
* @copyright 20
24 Justus Dieckmann and Tamara Gunkel, University of Münster
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use
mod_opencast\local\paella_transform
;
/**
* Automatic opencast videos filter class.
*
* @package filter_opencast
* @copyright 20
18 Tamara Gunkel
* @copyright 20
24 Justus Dieckmann and Tamara Gunkel, University of Münster
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class
filter_opencast
extends
moodle_text_filter
{
/**
* Get the content of the attribute $attributename from $tag.
* @param string $tag HTML-Tag.
* @param string $attributename Name of the attribute.
* @param string $attributecontentregex Regex of what the content of the attribute might be.
* @return string|null The content of the attribute or null, if it doesn't exist.
*/
private
static
function
get_attribute
(
string
$tag
,
string
$attributename
,
string
$attributecontentregex
=
'.*'
):
string
|
null
{
$pattern
=
"/
$attributename
=(['
\"
])(
$attributecontentregex
)
\\
1/"
;
preg_match
(
$pattern
,
$tag
,
$matches
);
return
$matches
[
2
]
??
null
;
}
/**
* Test whether $url matches one of the episodeurls.
* @param string $url The url to test.
* @param array $episodeurls array of [ocinstanceid, episoderegex, baseurl].
* @return array|null [ocinstanceid, episodeid] or null if there are no matches.
*/
private
static
function
test_url
(
string
$url
,
array
$episodeurls
)
:
array
|
null
{
foreach
(
$episodeurls
as
[
$ocinstanceid
,
$episoderegex
,
$baseurl
])
{
if
(
preg_match_all
(
$episoderegex
,
$url
,
$matches
))
{
return
[
$ocinstanceid
,
$matches
[
1
][
0
]];
}
}
return
null
;
}
/**
* Replaces Opencast videos embedded in <video> tags by the paella player.
*
* @param string $text
* @param array $options
* @return array|mixed|string|string[]|null
* @throws dml_exception
*/
public
function
filter
(
$text
,
array
$options
=
[])
{
global
$PAGE
,
$OUTPUT
;
$i
=
0
;
if
(
stripos
(
$text
,
'</
video>'
)
=
==
false
)
{
// Performance shortcut - if there
is
no </video> tag, nothing can match.
if
(
preg_match
(
'</(a|
video
)
>'
,
$text
)
!
==
1
)
{
// Performance shortcut - if there
are
no </video>
or </a>
tag
s
, nothing can match.
return
$text
;
}
foreach
(
\tool_opencast\local\settings_api
::
get_ocinstances
()
as
$ocinstance
)
{
// First section: (Relatively) quick check if there are episode urls in the text, and only look for these later.
// Improvable by combining all episode urls into one big regex if needed.
$ocinstances
=
\tool_opencast\local\settings_api
::
get_ocinstances
();
$occurrences
=
[];
foreach
(
$ocinstances
as
$ocinstance
)
{
$episodeurls
=
get_config
(
'filter_opencast'
,
'episodeurl_'
.
$ocinstance
->
id
);
if
(
!
$episodeurls
)
{
...
...
@@ -63,79 +92,131 @@ class filter_opencast extends moodle_text_filter {
$episodeurl
=
trim
(
$episodeurl
);
$urlparts
=
parse_url
(
$episodeurl
);
if
(
!
isset
(
$urlparts
[
'scheme'
])
||
!
isset
(
$urlparts
[
'host'
]))
{
continue
;
}
$baseurl
=
$urlparts
[
'scheme'
]
.
'://'
.
$urlparts
[
'host'
];
if
(
isset
(
$urlparts
[
'port'
]))
{
$baseurl
.
=
':'
.
$urlparts
[
'port'
];
}
if
(
empty
(
$episodeurl
)
||
stripos
(
$text
,
$baseurl
)
===
false
)
{
continue
;
if
(
self
::
str_contains
(
$text
,
$baseurl
))
{
$episoderegex
=
"/"
.
preg_quote
(
$episodeurl
,
"/"
)
.
"/"
;
$episoderegex
=
preg_replace
(
'/\\\\\[EPISODEID\\\]/'
,
'([0-9a-zA-Z\-]+)'
,
$episoderegex
);
$occurrences
[]
=
[
$ocinstance
->
id
,
$episoderegex
,
$baseurl
];
}
}
}
// Looking for tags.
$matches
=
preg_split
(
'/(<[^>]*>)/i'
,
$text
,
-
1
,
PREG_SPLIT_NO_EMPTY
|
PREG_SPLIT_DELIM_CAPTURE
);
if
(
$matches
)
{
$renderer
=
$PAGE
->
get_renderer
(
'filter_opencast'
);
$video
=
false
;
$width
=
false
;
$height
=
false
;
foreach
(
$matches
as
$match
)
{
if
(
empty
(
trim
(
$match
)))
{
continue
;
if
(
empty
(
$occurrences
))
{
return
$text
;
}
// Check if the match is a video tag.
if
(
substr
(
$match
,
0
,
6
)
===
"<video"
)
{
$video
=
true
;
preg_match
(
'/width="([0-9]+)"/'
,
$match
,
$width
);
preg_match
(
'/height="([0-9]+)"/'
,
$match
,
$height
);
$width
=
$width
?
$width
[
1
]
:
$width
;
$height
=
$height
?
$height
[
1
]
:
$height
;
}
else
if
(
$video
)
{
$video
=
false
;
if
(
substr
(
$match
,
0
,
7
)
===
"<source"
)
{
// Check if video is from opencast.
if
(
strpos
(
$match
,
$baseurl
)
===
false
)
{
$width
=
$height
=
false
;
continue
;
// Second section: splitting the text into tags (and stuff between tags), and search for relevant urls in <a> and <video>.
$matches
=
preg_split
(
'/(<[^>]*>)/i'
,
$text
,
-
1
,
PREG_SPLIT_NO_EMPTY
|
PREG_SPLIT_DELIM_CAPTURE
);
if
(
!
$matches
)
{
return
$text
;
}
// Extract url.
preg_match_all
(
'/<source[^>]+src=([\'"])(?<src>.+?)\1[^>]*>/i'
,
$match
,
$result
)
;
$i
=
0
;
$newtext
=
''
;
// Change url for loading the (Paella) Player.
$link
=
$result
[
'src'
][
0
];
$episode
=
null
;
$currenttag
=
null
;
$texttoreplace
=
''
;
$width
=
null
;
$height
=
null
;
// Get episode id from link.
$episoderegex
=
"/"
.
preg_quote
(
$episodeurl
,
"/"
)
.
"/"
;
$episoderegex
=
preg_replace
(
'/\\\\\[EPISODEID\\\]/'
,
'([0-9a-zA-Z\-]+)'
,
$episoderegex
);
$nummatches
=
preg_match_all
(
$episoderegex
,
$link
,
$episodeid
);
// We go through the complete text and transfer it match by match to $newtext.
// While we are going through interesting tags, $currenttag is set to 'video' or 'a' respectively.
// During that time, the matches are transferred to $texttoreplace instead. When we find the matching closing tag,
// ... we add either $texttoreplace to $newtext, or the html for the video player, if we found a matching opencast url.
foreach
(
$matches
as
$match
)
{
if
(
$currenttag
)
{
$texttoreplace
.
=
$match
;
if
(
self
::
str_starts_with
(
$match
,
"</
$currenttag
"
))
{
$replacement
=
null
;
if
(
$episode
)
{
$replacement
=
self
::
render_player
(
$episode
[
0
],
$episode
[
1
],
$i
++
,
$width
,
$height
);
}
if
(
$replacement
)
{
$newtext
.
=
$replacement
;
}
else
{
$newtext
.
=
$texttoreplace
;
}
$episode
=
null
;
$width
=
null
;
$height
=
null
;
$texttoreplace
=
null
;
$currenttag
=
null
;
}
else
if
(
!
$episode
&&
$currenttag
===
'video'
&&
self
::
str_starts_with
(
$match
,
'<source '
))
{
$src
=
self
::
get_attribute
(
$match
,
'src'
);
if
(
$src
)
{
$episode
=
self
::
test_url
(
$src
,
$occurrences
);
}
}
}
else
{
if
(
self
::
str_starts_with
(
$match
,
'<video '
))
{
$currenttag
=
'video'
;
$width
=
self
::
get_attribute
(
$match
,
'width'
,
'[0-9]+'
);
$height
=
self
::
get_attribute
(
$match
,
'height'
,
'[0-9]+'
);
$src
=
self
::
get_attribute
(
$match
,
'src'
);
if
(
$src
)
{
$episode
=
self
::
test_url
(
$src
,
$occurrences
);
}
}
else
if
(
self
::
str_starts_with
(
$match
,
'<a '
))
{
$src
=
self
::
get_attribute
(
$match
,
'href'
);
if
(
$src
)
{
$episode
=
self
::
test_url
(
$src
,
$occurrences
);
// Only set currenttag if there is a recognized url,
// ... so that nested <a> or <video> tags can be matched otherwise.
if
(
$episode
)
{
$currenttag
=
'a'
;
}
}
}
if
(
$currenttag
)
{
$texttoreplace
.
=
$match
;
}
else
{
$newtext
.
=
$match
;
}
}
}
if
(
!
$nummatches
)
{
$width
=
$height
=
false
;
continue
;
return
$newtext
;
}
$data
=
paella_transform
::
get_paella_data_json
(
$ocinstance
->
id
,
$episodeid
[
1
][
0
]);
/**
* Render HTML for embedding video player.
* @param int $ocinstanceid Id of ocinstance.
* @param string $episodeid Id opencast episode.
* @param int $playerid Unique id to assign to player element.
* @param int|null $width Optionally width for player.
* @param int|null $height Optionally height for player.
* @return string|null
*/
private
static
function
render_player
(
int
$ocinstanceid
,
string
$episodeid
,
int
$playerid
,
$width
=
null
,
$height
=
null
):
string
|
null
{
global
$OUTPUT
,
$PAGE
;
$data
=
paella_transform
::
get_paella_data_json
(
$ocinstanceid
,
$episodeid
);
if
(
!
$data
)
{
continue
;
return
null
;
}
// Collect the needed data being submitted to the template.
$mustachedata
=
new
stdClass
();
$mustachedata
->
playerid
=
'ocplayer_'
.
$i
++
;
$mustachedata
->
configurl
=
(
new
moodle_url
(
get_config
(
'filter_opencast'
,
'configurl_'
.
$ocinstance
->
id
)))
->
out
(
false
);
$mustachedata
->
themeurl
=
(
new
moodle_url
(
get_config
(
'mod_opencast'
,
'themeurl_'
.
$ocinstance
->
id
)))
->
out
(
false
);
if
(
strpos
(
$mustachedata
->
configurl
,
'http'
)
===
false
)
{
$mustachedata
->
configurl
=
(
new
moodle_url
(
$mustachedata
->
configurl
))
->
out
();
}
$mustachedata
->
playerid
=
'ocplayer_'
.
$playerid
;
$mustachedata
->
configurl
=
(
new
moodle_url
(
get_config
(
'filter_opencast'
,
'configurl_'
.
$ocinstanceid
)))
->
out
(
false
);
$mustachedata
->
themeurl
=
(
new
moodle_url
(
get_config
(
'mod_opencast'
,
'themeurl_'
.
$ocinstanceid
)))
->
out
(
false
);
$mustachedata
->
data
=
json_encode
(
$data
);
$mustachedata
->
width
=
$width
;
$mustachedata
->
height
=
$height
;
$mustachedata
->
modplayerpath
=
(
new
moodle_url
(
'/mod/opencast/player.html'
))
->
out
();
$mustachedata
->
modplayerpath
=
(
new
moodle_url
(
'/mod/opencast/player.html'
))
->
out
(
false
);
if
(
isset
(
$data
[
'streams'
]))
{
if
(
count
(
$data
[
'streams'
])
===
1
)
{
...
...
@@ -151,32 +232,34 @@ class filter_opencast extends moodle_text_filter {
$mustachedata
->
width
=
$height
*
(
$res
[
'w'
]
/
$res
[
'h'
]);
}
}
}
else
{
if
(
$width
&&
$height
)
{
$mustachedata
->
width
=
$width
;
$mustachedata
->
height
=
$height
;
}
}
$newtext
=
$renderer
->
render_player
(
$mustachedata
);
$renderer
=
$PAGE
->
get_renderer
(
'filter_opencast'
);
return
$renderer
->
render_player
(
$mustachedata
);
}
else
{
$newtext
=
$OUTPUT
->
render
(
new
\core\output\notification
(
return
$OUTPUT
->
render
(
new
\core\output\notification
(
get_string
(
'erroremptystreamsources'
,
'mod_opencast'
),
\core\output\notification
::
NOTIFY_ERROR
));
}
// Replace video tag.
$text
=
preg_replace
(
'/<video(?:(?!<\/video>).)*?'
.
preg_quote
(
$match
,
'/'
)
.
'.*?<\/video>/s'
,
$newtext
,
$text
,
1
);
}
$width
=
$height
=
false
;
}
}
}
}
/**
* Polyfill for str_contains for PHP 7.
* @param string $haystack
* @param string $needle
* @return bool
*/
private
static
function
str_contains
(
string
$haystack
,
string
$needle
):
bool
{
return
strpos
(
$haystack
,
$needle
)
!==
false
;
}
// Return the same string except processed by the above.
return
$text
;
/**
* Polyfill for str_starts_with for PHP 7.
* @param string $haystack
* @param string $needle
* @return bool
*/
private
static
function
str_starts_with
(
string
$haystack
,
string
$needle
):
bool
{
return
strpos
(
$haystack
,
$needle
)
===
0
;
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment