diff --git a/system/typemill/Extensions/MediaExtension.php b/system/typemill/Extensions/MediaExtension.php
index 1f1f6f1..2e4a877 100644
--- a/system/typemill/Extensions/MediaExtension.php
+++ b/system/typemill/Extensions/MediaExtension.php
@@ -110,5 +110,68 @@ class MediaExtension implements EventSubscriberInterface
$shortcode->setData($html);
}
+
+ if(is_array($shortcodeArray) && $shortcodeArray['name'] == 'audio' && isset($shortcodeArray['params']['path']))
+ {
+ $relUrl = $shortcodeArray['params']['path'];
+ $relUrl = '/' . trim($relUrl, '/');
+
+ # Convert the relative URL to an absolute file path
+ $filePath = $this->rootpath . $relUrl;
+
+ # check file exists
+ if(!file_exists($filePath))
+ {
+ $html = '
File not found
';
+ }
+ else
+ {
+ # Get file extension using pathinfo()
+ $fileInfo = pathinfo($filePath);
+ $extension = strtolower($fileInfo['extension']); // Get file extension and convert to lowercase
+ $absUrl = $this->baseurl . $relUrl;
+
+ # Determine the correct file type for the video tag
+ $type = '';
+ switch ($extension) {
+ case 'mp3':
+ $type = 'mp3';
+ break;
+ case 'ogg':
+ $type = 'ogg';
+ break;
+ default:
+ $html = 'Unsupported file type
';
+ return; // Exit if file type is not supported
+ }
+
+ $preload = 'none';
+ if(isset($shortcodeArray['params']['preload']) && ($shortcodeArray['params']['preload'] == 'auto' or $shortcodeArray['params']['preload'] == 'metadata'))
+ {
+ $preload = $shortcodeArray['params']['preload'];
+ }
+
+ $width = $shortcodeArray['params']['width'] ?? '500';
+ if (!preg_match('/^(\d+)(px|%)?$/', $width))
+ {
+ $width = '500';
+ }
+
+ $html = '';
+ }
+
+ $shortcode->setData($html);
+ }
}
+
}
\ No newline at end of file
diff --git a/system/typemill/author/js/vue-blox-components.js b/system/typemill/author/js/vue-blox-components.js
index 68c70f0..86df4df 100644
--- a/system/typemill/author/js/vue-blox-components.js
+++ b/system/typemill/author/js/vue-blox-components.js
@@ -2640,6 +2640,299 @@ bloxeditor.component('video-component', {
}
})
+bloxeditor.component('audio-component', {
+ props: ['markdown', 'disabled', 'index'],
+ components: {
+ medialib: medialib
+ },
+ template: `
+
+
+
+
+
+
+ {{ $filters.translate('upload audio') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`,
+ data: function(){
+ return {
+ maxsize: 100, // megabyte
+ showmedialib: false,
+ load: false,
+ filemeta: false,
+ fileextension: '',
+ allowedExtensions: ['mp3', 'ogg'],
+ fileurl: '',
+ width: '500px',
+ fileid: '',
+ savefile: false,
+ preload: 'none',
+ }
+ },
+ mounted: function() {
+ eventBus.$on('beforeSave', this.beforeSave);
+
+ this.$refs.markdown.focus();
+
+ if (this.markdown)
+ {
+ this.filemeta = true;
+
+ var fileurl = this.markdown.match(/path="(.*?)"/);
+ if (fileurl && fileurl[1])
+ {
+ this.fileurl = fileurl[1];
+ }
+
+ var width = this.markdown.match(/width="(.*?)"/);
+ if (width && width[1])
+ {
+ this.width = width[1];
+ }
+
+ var preload = this.markdown.match(/preload="(.*?)"/);
+ if (preload && preload[1])
+ {
+ this.preload = preload[1];
+ }
+ }
+ },
+ methods: {
+ addFromMedialibFunction(file)
+ {
+ this.showmedialib = false;
+ this.savefile = false;
+ this.filemeta = true;
+
+ if (this.allowedExtensions.includes(file.info.extension.toLowerCase()))
+ {
+ this.filetitle = file.name;
+ this.fileextension = file.info.extension.toLowerCase();
+ this.fileurl = file.url;
+ }
+ else
+ {
+ let message = "Unsupported file type. Please select a valid audio file (mp3, ogg).";
+ eventBus.$emit('publishermessage', message);
+ return;
+ }
+
+ this.createmarkdown();
+ },
+ openmedialib()
+ {
+ this.showmedialib = true;
+ },
+ isChecked(classname)
+ {
+ if(this.fileclass == classname)
+ {
+ return ' checked';
+ }
+ },
+ updatemarkdown(event, url)
+ {
+ this.$emit('updateMarkdownEvent', event.target.value);
+ },
+ createmarkdown()
+ {
+ var errors = false;
+ var filemarkdown = false;
+
+ if (this.fileurl !== '')
+ {
+ if (this.fileurl.length < 101)
+ {
+ var width = this.width ? ' width="' + this.width + '"' : '';
+ var preload = this.preload ? ' preload="' + this.preload + '"' : ' preload="none"';
+
+ filemarkdown = '[:audio path="' + this.fileurl + '"' + width + preload + ' :]';
+ }
+ else
+ {
+ errors = this.$filters.translate('Maximum size of file link is 100 characters');
+ }
+ }
+
+ if (errors)
+ {
+ eventBus.$emit('publishermessage', this.$filters.translate(errors));
+ }
+ else if (filemarkdown)
+ {
+ this.$emit('updateMarkdownEvent', filemarkdown);
+ this.compmarkdown = filemarkdown;
+ }
+ },
+ onFileChange( e )
+ {
+ if(e.target.files.length > 0)
+ {
+ let uploadedFile = e.target.files[0];
+
+ let allowedAudioTypes = ['audio/mpeg', 'audio/ogg'];
+ if (!allowedAudioTypes.includes(uploadedFile.type)) {
+ let message = "Unsupported file type. Please select an audio file (mp3, ogg).";
+ eventBus.$emit('publishermessage', message);
+ return;
+ }
+
+ let size = uploadedFile.size / 1024 / 1024;
+
+ if (size > this.maxsize)
+ {
+ let message = "The maximal size of a file is " + this.maxsize + " MB";
+ eventBus.$emit('publishermessage', message);
+ return;
+ }
+ else
+ {
+ self = this;
+
+ self.load = true;
+
+ let reader = new FileReader();
+ reader.readAsDataURL(uploadedFile);
+ reader.onload = function(e) {
+
+ tmaxios.post('/api/v1/file',{
+ 'url': data.urlinfo.route,
+ 'file': e.target.result,
+ 'name': uploadedFile.name,
+ })
+ .then(function (response) {
+
+ self.load = false;
+
+ self.filemeta = true;
+ self.savefile = true;
+ self.filetitle = response.data.fileinfo.title;
+ self.fileextension = response.data.fileinfo.extension;
+ self.fileurl = response.data.filepath;
+ self.selectedrole = '';
+
+ self.createmarkdown();
+ })
+ .catch(function (error)
+ {
+ self.load = false;
+ if(error.response)
+ {
+ let message = self.$filters.translate(error.response.data.message);
+ eventBus.$emit('publishermessage', message);
+ }
+ });
+ }
+ }
+ }
+ },
+ beforeSave()
+ {
+ /* publish file before you save markdown */
+
+ if(!this.fileurl)
+ {
+ let message = this.$filters.translate('file is missing.');
+ eventBus.$emit('publishermessage', message);
+
+ return;
+ }
+
+ const fileExtension = this.fileurl.split('.').pop().toLowerCase();
+
+ if (!this.allowedExtensions.includes(fileExtension))
+ {
+ let message = this.$filters.translate('Unsupported file format. Only MP3, and OGG files are allowed.');
+ eventBus.$emit('publishermessage', message);
+
+ return;
+ }
+
+ if(!this.savefile)
+ {
+ this.createmarkdown();
+ this.$emit('saveBlockEvent');
+ }
+ else
+ {
+ var self = this;
+
+ tmaxios.put('/api/v1/file',{
+ 'url': data.urlinfo.route,
+ 'file': this.fileurl,
+ })
+ .then(function (response)
+ {
+ self.fileurl = response.data.path;
+
+ self.createmarkdown();
+
+ self.$emit('saveBlockEvent');
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ let message = self.$filters.translate(error.response.data.message);
+ eventBus.$emit('publishermessage', message);
+ }
+ });
+ }
+ },
+ }
+})
+
bloxeditor.component('shortcode-component', {
props: ['markdown', 'disabled', 'index'],
data: function(){
diff --git a/system/typemill/author/js/vue-blox-config.js b/system/typemill/author/js/vue-blox-config.js
index 6f8f211..684936b 100644
--- a/system/typemill/author/js/vue-blox-config.js
+++ b/system/typemill/author/js/vue-blox-config.js
@@ -74,6 +74,13 @@ const determiner = {
}
return false;
},
+ audio: function(block,lines,firstChar,secondChar,thirdChar){
+ if (lines[0].startsWith('[:audio'))
+ {
+ return "audio-component";
+ }
+ return false;
+ },
code: function(block,lines,firstChar,secondChar,thirdChar){
if( firstChar == '`' && secondChar == '`' && thirdChar == '`')
{
@@ -114,6 +121,7 @@ const bloxFormats = {
notice: { label: '', title: 'Notice', component: 'notice-component' },
image: { label: '', title: 'Image', component: 'image-component' },
video: { label: '', title: 'Video', component: 'video-component' },
+ audio: { label: '', title: 'Audio', component: 'audio-component' },
file: { label: '', title: 'File', component: 'file-component' },
toc: { label: '', title: 'Table of Contents', component: 'toc-component' },
hr: { label: '', title: 'Horizontal Line', component: 'hr-component' },
diff --git a/system/typemill/author/partials/symbols.twig b/system/typemill/author/partials/symbols.twig
index 317d941..39f410f 100644
--- a/system/typemill/author/partials/symbols.twig
+++ b/system/typemill/author/partials/symbols.twig
@@ -46,6 +46,10 @@
{{ translate('VIDEO') }}
+
+ {{ translate('AUDIO') }}
+
+
{{ translate('QUOTES') }}
diff --git a/system/typemill/settings/defaults.yaml b/system/typemill/settings/defaults.yaml
index 2a1beae..47dde9d 100644
--- a/system/typemill/settings/defaults.yaml
+++ b/system/typemill/settings/defaults.yaml
@@ -16,6 +16,7 @@ formats:
- 'notice'
- 'image'
- 'video'
+ - 'audio'
- 'file'
- 'toc'
- 'hr'
diff --git a/system/typemill/settings/system.yaml b/system/typemill/settings/system.yaml
index ed05967..21816b0 100644
--- a/system/typemill/settings/system.yaml
+++ b/system/typemill/settings/system.yaml
@@ -126,6 +126,7 @@ fieldsetwriting:
'notice': 'notice'
'image': 'image'
'video': 'video'
+ 'audio': 'audio'
'file': 'file'
'toc': 'table of contents'
'hr': 'horizontal line'