From 227cbf69f371f38602303c9a1e4e3504b58d9297 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 14 Jun 2023 18:44:27 +0200 Subject: [PATCH 01/17] Audio wave form implementation with beta implementation of zoom --- .../webapp/WEB-INF/resources/css/kitodo.css | 2 + .../resources/js/media_detail_audio.js | 65 +++++++++++++++++++ .../partials/media-detail.xhtml | 5 +- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index de14da398ea..e7d467fb874 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3024,6 +3024,8 @@ Column content width: 100%; align-items: center; justify-content: center; + flex-direction: column; + gap: 16px; } #imagePreviewForm\:mediaDetail .mediaPreviewItem { diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js new file mode 100644 index 00000000000..1563ae99ae3 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js @@ -0,0 +1,65 @@ +/** + * (c) Kitodo. Key to digital objects e. V. + * + * This file is part of the Kitodo project. + * + * It is licensed under GNU General Public License version 3 or later. + * + * For the full copyright and license information, please read the + * GPL3-License.txt file that was distributed with this source code. + */ + +import WaveSurfer from 'https://unpkg.com/wavesurfer.js@beta' + +let audios = document.querySelectorAll('audio.mediaPreviewItem'); + +let audio = audios[0]; +audio.src = audio.currentSrc; + +let waveTools = document.createElement("div"); +waveTools.setAttribute("id", "wave-tools"); +waveTools.style.width = "90%"; +waveTools.innerHTML = ""; +audio.parentNode.insertBefore(waveTools, audio); + +let waveContainer = document.createElement("div"); +waveContainer.setAttribute("id", "wave-container"); +waveContainer.onclick = function(){wavesurfer.playPause()} +waveContainer.style.width = "90%"; +audio.parentNode.insertBefore(waveContainer, audio); + +const wavesurfer = WaveSurfer.create({ + container: document.getElementById(waveContainer.getAttribute("id")), + height: 100, + waveColor: "#f3f3f3", + progressColor: "#ff4e00", + cursorColor: "#ffffff", + media: audio, + minPxPerSec: 10, +}); + +wavesurfer.once('decode', () => { + let waveToolsContainer = document.getElementById(waveTools.getAttribute("id")) + const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') + + waveToolsSlider.addEventListener('input', (e) => { + const minPxPerSec = e.target.valueAsNumber + wavesurfer.zoom(minPxPerSec) + }) + + waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => { + input.onchange = (e) => { + wavesurfer.setOptions({ + [input.value]: e.target.checked, + }) + } + }) +}) + + +wavesurfer.on("error", function (e) { + console.warn(e); +}); + +// Load audio from URL +//wavesurfer.load(audio); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index b11360fff84..e039d823444 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -16,7 +16,8 @@ xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions" xmlns:p="http://primefaces.org/ui" - xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> + xmlns:ui="http://xmlns.jcp.org/jsf/facelets" + xmlns:a="http://xmlns.jcp.org/jsf/passthrough"> + + Date: Wed, 21 Jun 2023 17:18:37 +0200 Subject: [PATCH 02/17] Add wave tools --- .../webapp/WEB-INF/resources/css/kitodo.css | 21 +++++++++++++++- .../resources/js/media_detail_audio.js | 10 ++------ .../partials/media-detail.xhtml | 24 +++++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index e7d467fb874..0479f889acf 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3025,13 +3025,32 @@ Column content align-items: center; justify-content: center; flex-direction: column; - gap: 16px; + gap: 24px; } #imagePreviewForm\:mediaDetail .mediaPreviewItem { max-height: 100%; } +#imagePreviewForm\:mediaDetail #waveTools{ + display: flex; + gap: 30px; + justify-content: center; + align-items: center; + padding: 14px 16px; + background: var(--trans-blue); + border-radius: 3px; +} + +#imagePreviewForm\:mediaDetail #waveTools > div > button:first-child { + margin-left: 0px; +} + +#imagePreviewForm\:mediaDetail #waveTools > div > button { + margin-left: 10px; +} + + #imagePreviewForm .mediaListIconItem { height: 100%; width: 100%; diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js index 1563ae99ae3..3b2dd45f0e1 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js @@ -16,12 +16,6 @@ let audios = document.querySelectorAll('audio.mediaPreviewItem'); let audio = audios[0]; audio.src = audio.currentSrc; -let waveTools = document.createElement("div"); -waveTools.setAttribute("id", "wave-tools"); -waveTools.style.width = "90%"; -waveTools.innerHTML = ""; -audio.parentNode.insertBefore(waveTools, audio); - let waveContainer = document.createElement("div"); waveContainer.setAttribute("id", "wave-container"); waveContainer.onclick = function(){wavesurfer.playPause()} @@ -35,11 +29,11 @@ const wavesurfer = WaveSurfer.create({ progressColor: "#ff4e00", cursorColor: "#ffffff", media: audio, - minPxPerSec: 10, + minPxPerSec: 0, }); wavesurfer.once('decode', () => { - let waveToolsContainer = document.getElementById(waveTools.getAttribute("id")) + let waveToolsContainer = document.getElementById("waveTools") const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') waveToolsSlider.addEventListener('input', (e) => { diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index e039d823444..6fb46e92850 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -25,6 +25,30 @@ + +
+
+ +
+
+ +
+
+ + + + +
+
+ From 43adb599e2c74778aa8c1adad017d10b6dfe3f5b Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Tue, 19 Sep 2023 11:17:50 +0200 Subject: [PATCH 03/17] Add wavesurfer lib and improve rendering of scripts --- .../WEB-INF/resources/js/libs/wavesurfer/wavesurfer.esm.js | 2 ++ .../main/webapp/WEB-INF/resources/js/media_detail_audio.js | 5 +---- .../includes/metadataEditor/partials/media-detail.xhtml | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/wavesurfer.esm.js diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/wavesurfer.esm.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/wavesurfer.esm.js new file mode 100644 index 00000000000..013504e3ff4 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/wavesurfer.esm.js @@ -0,0 +1,2 @@ +// wavesurfer 7.3.2 https://wavesurfer-js.org +function t(t,e,i,s){return new(i||(i=Promise))((function(n,r){function o(t){try{h(s.next(t))}catch(t){r(t)}}function a(t){try{h(s.throw(t))}catch(t){r(t)}}function h(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}h((s=s.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const e={decode:function(e,i){return t(this,void 0,void 0,(function*(){const t=new AudioContext({sampleRate:i});return t.decodeAudioData(e).finally((()=>t.close()))}))},createBuffer:function(t,e){return"number"==typeof t[0]&&(t=[t]),function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let s=0;for(let t=0;ts&&(s=i)}for(const e of t)for(let t=0;tnull==t?void 0:t[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};const i={fetchBlob:function(e,i,s){var n,r;return t(this,void 0,void 0,(function*(){const o=yield fetch(e,s);{const e=null===(n=o.clone().body)||void 0===n?void 0:n.getReader(),s=Number(null===(r=o.headers)||void 0===r?void 0:r.get("Content-Length"));let a=0;const h=(n,r)=>t(this,void 0,void 0,(function*(){if(n)return;a+=(null==r?void 0:r.length)||0;const t=Math.round(a/s*100);return i(t),null==e?void 0:e.read().then((({done:t,value:e})=>h(t,e)))}));null==e||e.read().then((({done:t,value:e})=>h(t,e)))}return o.blob()}))}};class s{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class n extends s{constructor(t){super(),t.media?this.media=t.media:this.media=document.createElement("audio"),t.mediaControls&&(this.media.controls=!0),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&this.onceMediaEvent("canplay",(()=>{null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}))}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){const t=this.getSrc();t.startsWith("blob:")&&URL.revokeObjectURL(t)}setSrc(t,e){if(this.getSrc()===t)return;this.revokeSrc();const i=e instanceof Blob?URL.createObjectURL(e):t;this.media.src=i,this.media.load()}destroy(){this.media.pause(),this.revokeSrc(),this.media.src="",this.media.load()}play(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}class r extends s{constructor(t,e){let i;if(super(),this.timeouts=[],this.isScrolling=!1,this.audioData=null,this.resizeObserver=null,this.isDragging=!1,this.options=t,"string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");this.parent=i;const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),this.cursor=n.querySelector(".cursor"),e&&n.appendChild(e),this.initEvents()}initEvents(){const t=t=>{const e=this.wrapper.getBoundingClientRect(),i=t.clientX-e.left,s=t.clientX-e.left;return[i/e.width,s/e.height]};this.wrapper.addEventListener("click",(e=>{const[i,s]=t(e);this.emit("click",i,s)})),this.wrapper.addEventListener("dblclick",(e=>{const[i,s]=t(e);this.emit("dblclick",i,s)})),this.options.dragToSeek&&this.initDrag(),this.scrollContainer.addEventListener("scroll",(()=>{const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer,s=t/e,n=(t+i)/e;this.emit("scroll",s,n)}));const e=this.createDelay(100);this.resizeObserver=new ResizeObserver((()=>{e((()=>this.reRender()))})),this.resizeObserver.observe(this.scrollContainer)}initDrag(){!function(t,e,i,s,n=5){let r=()=>{};if(!t)return r;const o=o=>{if(2===o.button)return;o.preventDefault(),o.stopPropagation();let a=o.clientX,h=o.clientY,l=!1;const d=s=>{s.preventDefault(),s.stopPropagation();const r=s.clientX,o=s.clientY;if(l||Math.abs(r-a)>=n||Math.abs(o-h)>=n){const{left:s,top:n}=t.getBoundingClientRect();l||(l=!0,null==i||i(a-s,h-n)),e(r-a,o-h,r-s,o-n),a=r,h=o}},c=t=>{l&&(t.preventDefault(),t.stopPropagation())},u=()=>{l&&(null==s||s()),r()};document.addEventListener("pointermove",d),document.addEventListener("pointerup",u),document.addEventListener("pointerleave",u),document.addEventListener("click",c,!0),r=()=>{document.removeEventListener("pointermove",d),document.removeEventListener("pointerup",u),document.removeEventListener("pointerleave",u),setTimeout((()=>{document.removeEventListener("click",c,!0)}),10)}};t.addEventListener("pointerdown",o)}(this.wrapper,((t,e,i)=>{this.emit("drag",Math.max(0,Math.min(1,i/this.wrapper.getBoundingClientRect().width)))}),(()=>this.isDragging=!0),(()=>this.isDragging=!1))}getHeight(){return null==this.options.height?128:isNaN(Number(this.options.height))?"auto"===this.options.height&&this.parent.clientHeight||128:Number(this.options.height)}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.innerHTML=`\n \n\n
\n
\n
\n
\n
\n
\n
\n `,[t,e]}setOptions(t){this.options=t,this.reRender()}getWrapper(){return this.wrapper}getScroll(){return this.scrollContainer.scrollLeft}destroy(){var t;this.container.remove(),null===(t=this.resizeObserver)||void 0===t||t.disconnect()}createDelay(t=10){const e={};return this.timeouts.push(e),i=>{e.timeout&&clearTimeout(e.timeout),e.timeout=setTimeout(i,t)}}convertColorValues(t){if(!Array.isArray(t))return t||"";if(t.length<2)return t[0]||"";const e=document.createElement("canvas"),i=e.getContext("2d").createLinearGradient(0,0,0,e.height),s=1/(t.length-1);return t.forEach(((t,e)=>{const n=e*s;i.addColorStop(n,t)})),i}renderBarWaveform(t,e,i,s){const n=t[0],r=t[1]||t[0],o=n.length,{width:a,height:h}=i.canvas,l=h/2,d=window.devicePixelRatio||1,c=e.barWidth?e.barWidth*d:1,u=e.barGap?e.barGap*d:e.barWidth?c/2:0,p=e.barRadius||0,m=a/(c+u)/o,g=p&&"roundRect"in i?"roundRect":"rect";i.beginPath();let v=0,f=0,b=0;for(let t=0;t<=o;t++){const o=Math.round(t*m);if(o>v){const t=Math.round(f*l*s),n=t+Math.round(b*l*s)||1;let r=l-t;"top"===e.barAlign?r=0:"bottom"===e.barAlign&&(r=h-n),i[g](v*(c+u),r,c,n,p),v=o,f=0,b=0}const a=Math.abs(n[t]||0),d=Math.abs(r[t]||0);a>f&&(f=a),d>b&&(b=d)}i.fill(),i.closePath()}renderLineWaveform(t,e,i,s){const n=e=>{const n=t[e]||t[0],r=n.length,{height:o}=i.canvas,a=o/2,h=i.canvas.width/r;i.moveTo(0,a);let l=0,d=0;for(let t=0;t<=r;t++){const r=Math.round(t*h);if(r>l){const t=a+(Math.round(d*a*s)||1)*(0===e?-1:1);i.lineTo(l,t),l=r,d=0}const o=Math.abs(n[t]||0);o>d&&(d=o)}i.lineTo(l,a)};i.beginPath(),n(0),n(1),i.fill(),i.closePath()}renderWaveform(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor),e.renderFunction)return void e.renderFunction(t,i);let s=e.barHeight||1;if(e.normalize){const e=Array.from(t[0]).reduce(((t,e)=>Math.max(t,Math.abs(e))),0);s=e?1/e:1}e.barWidth||e.barGap||e.barAlign?this.renderBarWaveform(t,e,i,s):this.renderLineWaveform(t,e,i,s)}renderSingleCanvas(t,e,i,s,n,r,o,a){const h=window.devicePixelRatio||1,l=document.createElement("canvas"),d=t[0].length;l.width=Math.round(i*(r-n)/d),l.height=s*h,l.style.width=`${Math.floor(l.width/h)}px`,l.style.height=`${s}px`,l.style.left=`${Math.floor(n*i/h/d)}px`,o.appendChild(l);const c=l.getContext("2d");this.renderWaveform(t.map((t=>t.slice(n,r))),e,c);const u=l.cloneNode();a.appendChild(u);const p=u.getContext("2d");l.width>0&&l.height>0&&p.drawImage(l,0,0),p.globalCompositeOperation="source-in",p.fillStyle=this.convertColorValues(e.progressColor),p.fillRect(0,0,l.width,l.height)}renderChannel(t,e,i){const s=document.createElement("div"),n=this.getHeight();s.style.height=`${n}px`,this.canvasWrapper.style.minHeight=`${n}px`,this.canvasWrapper.appendChild(s);const o=s.cloneNode();this.progressWrapper.appendChild(o);const{scrollLeft:a,scrollWidth:h,clientWidth:l}=this.scrollContainer,d=t[0].length,c=d/h;let u=Math.min(r.MAX_CANVAS_WIDTH,l);if(e.barWidth||e.barGap){const t=e.barWidth||.5,i=t+(e.barGap||t/2);u%i!=0&&(u=Math.floor(u/i)*i)}const p=Math.floor(Math.abs(a)*c),m=Math.floor(p+u*c),g=m-p,v=(r,a)=>{this.renderSingleCanvas(t,e,i,n,Math.max(0,r),Math.min(a,d),s,o)},f=this.createDelay(),b=this.createDelay(),y=(t,e)=>{v(t,e),t>0&&f((()=>{y(t-g,e-g)}))},C=(t,e)=>{v(t,e),e{C(t+g,e+g)}))};y(p,m),mt.timeout&&clearTimeout(t.timeout))),this.timeouts=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",this.wrapper.style.width="";const e=window.devicePixelRatio||1,i=this.scrollContainer.clientWidth,s=Math.ceil(t.duration*(this.options.minPxPerSec||0));this.isScrolling=s>i;const n=this.options.fillParent&&!this.isScrolling,r=(n?i:s)*e;if(this.wrapper.style.width=n?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.options.splitChannels)for(let e=0;e1&&e.push(t.getChannelData(1)),this.renderChannel(e,this.options,r)}this.audioData=t,this.emit("render")}reRender(){if(!this.audioData)return;const t=this.progressWrapper.clientWidth;this.render(this.audioData);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2;if(r>s+(e&&this.options.autoCenter&&!this.isDragging?o:i)||r=t&&r{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}}const a={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,dragToSeek:!1,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class h extends n{static create(t){return new h(t)}constructor(t){var e,i;super({media:t.media,mediaControls:t.mediaControls,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.subscriptions=[],this.options=Object.assign({},a,t),this.timer=new o;const s=t.media?void 0:this.getMediaElement();this.renderer=new r(this.options,s),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initPlugins();const n=this.options.url||(null===(e=this.options.media)||void 0===e?void 0:e.currentSrc)||(null===(i=this.options.media)||void 0===i?void 0:i.src);n&&this.load(n,this.options.peaks,this.options.duration)}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",t),this.emit("audioprocess",t)})))}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("emptied",(()=>{this.timer.stop()})),this.onMediaEvent("ended",(()=>{this.emit("finish")})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",((t,e)=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",t*this.getDuration()),this.emit("click",t,e))})),this.renderer.on("dblclick",((t,e)=>{this.emit("dblclick",t,e)})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})),this.renderer.on("render",(()=>{this.emit("redraw")})));{let t;this.subscriptions.push(this.renderer.on("drag",(e=>{this.options.interact&&(this.renderer.renderProgress(e),clearTimeout(t),t=setTimeout((()=>{this.seekTo(e)}),this.isPlaying()?0:200),this.emit("interaction",e*this.getDuration()),this.emit("drag",e))})))}}initPlugins(){var t;(null===(t=this.options.plugins)||void 0===t?void 0:t.length)&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}setOptions(t){this.options=Object.assign({},this.options,t),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate),null!=t.mediaControls&&(this.getMediaElement().controls=t.mediaControls)}registerPlugin(t){return t.init(this),this.plugins.push(t),this.subscriptions.push(t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))}))),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}loadAudio(s,n,r,o){return t(this,void 0,void 0,(function*(){if(this.emit("load",s),this.isPlaying()&&this.pause(),this.decodedData=null,!n&&!r){const t=t=>this.emit("loading",t);n=yield i.fetchBlob(s,t,this.options.fetchParams)}if(this.setSrc(s,n),r)o=(yield Promise.resolve(o||this.getDuration()))||(yield new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))})))||(yield Promise.resolve(0)),this.decodedData=e.createBuffer(r,o);else if(n){const t=yield n.arrayBuffer();this.decodedData=yield e.decode(t,this.options.sampleRate)}this.emit("decode",this.getDuration()),this.decodedData&&this.renderer.render(this.decodedData),this.emit("ready",this.getDuration())}))}load(e,i,s){return t(this,void 0,void 0,(function*(){yield this.loadAudio(e,void 0,i,s)}))}loadBlob(e,i,s){return t(this,void 0,void 0,(function*(){yield this.loadAudio("blob",e,i,s)}))}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}exportPeaks({channels:t=1,maxLength:e=8e3,precision:i=1e4}={}){if(!this.decodedData)throw new Error("The audio has not been decoded yet");const s=Math.min(t,this.decodedData.numberOfChannels),n=[];for(let t=0;tt.destroy())),this.subscriptions.forEach((t=>t())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}export{h as default}; diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js index 3b2dd45f0e1..71b11b61af0 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js @@ -9,7 +9,7 @@ * GPL3-License.txt file that was distributed with this source code. */ -import WaveSurfer from 'https://unpkg.com/wavesurfer.js@beta' +import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf' let audios = document.querySelectorAll('audio.mediaPreviewItem'); @@ -54,6 +54,3 @@ wavesurfer.once('decode', () => { wavesurfer.on("error", function (e) { console.warn(e); }); - -// Load audio from URL -//wavesurfer.load(audio); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index 6fb46e92850..2a9aa53866d 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -61,8 +61,8 @@
- - + +
Date: Wed, 20 Sep 2023 19:11:37 +0200 Subject: [PATCH 04/17] Add waveform configuration in project form --- .../kitodo/data/database/beans/Project.java | 14 ++++++++++++-- ...d_audio_media_view_waveform_to_project.sql | 14 ++++++++++++++ .../kitodo/production/forms/ProjectForm.java | 19 +++++++++++++++++++ .../resources/messages/messages_de.properties | 1 + .../resources/messages/messages_en.properties | 1 + .../projectEdit/projectEditMets.xhtml | 6 ++++++ 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java index faf64e45948..6e31a64a32e 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java @@ -128,19 +128,21 @@ public class Project extends BaseIndexedBean implements Comparable { private Folder preview; /** - * Folder with media to use for the video preview. + * Folder with media to use for the audio preview. */ @ManyToOne @JoinColumn(name = "preview_audio_folder_id", foreignKey = @ForeignKey(name = "FK_project_preview_audio_folder_id")) private Folder audioPreview; /** - * Folder with media to use for the video viewer. + * Folder with media to use for the audio viewer. */ @ManyToOne @JoinColumn(name = "mediaView_audio_folder_id", foreignKey = @ForeignKey(name = "FK_project_mediaView_audio_folder_id")) private Folder audioMediaView; + @Column(name = "mediaView_audio_waveform") + private Boolean audioMediaViewWaveform = true; /** * Folder with media to use for the video preview. @@ -553,6 +555,14 @@ public void setAudioMediaView(Folder audioMediaView) { this.audioMediaView = audioMediaView; } + public boolean isAudioMediaViewWaveform() { + return audioMediaViewWaveform; + } + + public void setAudioMediaViewWaveform(boolean audioMediaViewWaveform) { + this.audioMediaViewWaveform = audioMediaViewWaveform; + } + /** * Returns the folder to use for video preview. * diff --git a/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql new file mode 100644 index 00000000000..58936814251 --- /dev/null +++ b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql @@ -0,0 +1,14 @@ +-- +-- (c) Kitodo. Key to digital objects e. V. +-- +-- This file is part of the Kitodo project. +-- +-- It is licensed under GNU General Public License version 3 or later. +-- +-- For the full copyright and license information, please read the +-- GPL3-License.txt file that was distributed with this source code. +-- + +-- +-- Migration: Add column for state of audio waveform in media view to project table. +ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 1 diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/ProjectForm.java b/Kitodo/src/main/java/org/kitodo/production/forms/ProjectForm.java index d03d39cb624..3a4011f548d 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/ProjectForm.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/ProjectForm.java @@ -674,6 +674,25 @@ public void setAudioMediaView(String audioMediaView) { project.setAudioMediaView(getFolderMap().get(audioMediaView)); } + /** + * Returns the state of the audio media view waveform. + * + * @return True if enabled + */ + public boolean isAudioMediaViewWaveform() { + return project.isAudioMediaViewWaveform(); + } + + /** + * Sets the state of the audio media view waveform. + * + * @param audioMediaViewWaveform True if enabled + * + */ + public void setAudioMediaViewWaveform(boolean audioMediaViewWaveform) { + project.setAudioMediaViewWaveform(audioMediaViewWaveform); + } + /** * Returns the folder to use for the video media view. * diff --git a/Kitodo/src/main/resources/messages/messages_de.properties b/Kitodo/src/main/resources/messages/messages_de.properties index 93a139d10b9..760ae7488a3 100644 --- a/Kitodo/src/main/resources/messages/messages_de.properties +++ b/Kitodo/src/main/resources/messages/messages_de.properties @@ -463,6 +463,7 @@ folderUse.audioPreview=Als Vorschau f\u00fcr Audios verwenden folderUse.audioPreview.disabled=Vorschau f\u00fcr Audios deaktiviert folderUse.audioMediaView=F\u00FCr die Medienansicht f\u00fcr Audios verwenden folderUse.audioMediaView.disabled=Medienansicht f\u00fcr Audios deaktiviert +folderUse.audioMediaViewWaveform=Wellenform in der Medienansicht f\u00fcr Audios anzeigen folderUse.generatorSource=Als Quelle zum Generieren von Inhalten verwenden folderUse.generatorSource.disabled=Generieren von Inhalten deaktiviert folderUse.mediaView=F\u00FCr die Medienansicht verwenden diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index 6b9172fa659..0a9778894f9 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -464,6 +464,7 @@ folderUse.audioPreview=Use as preview for audios folderUse.audioPreview.disabled=Preview for audios disabled folderUse.audioMediaView=Use for the media view for audios folderUse.audioMediaView.disabled=Media view for audios disabled +folderUse.audioMediaViewWaveform=Show waveform in media view for audios folderUse.generatorSource=Use as source to generate contents folderUse.generatorSource.disabled=Content generation disabled folderUse.mediaView=Use for the media view diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projectEdit/projectEditMets.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projectEdit/projectEditMets.xhtml index 72b7a537209..12627845774 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projectEdit/projectEditMets.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projectEdit/projectEditMets.xhtml @@ -207,6 +207,12 @@ + + + Date: Thu, 21 Sep 2023 16:14:55 +0200 Subject: [PATCH 05/17] Activate waveform at the project --- .../kitodo/data/database/beans/Project.java | 15 +++++- ...d_audio_media_view_waveform_to_project.sql | 2 +- .../forms/dataeditor/GalleryPanel.java | 7 +++ .../webapp/WEB-INF/resources/css/kitodo.css | 10 ++++ .../resources/js/media_detail_audio.js | 12 +++++ .../partials/media-detail.xhtml | 50 ++++++++++--------- .../projectEdit/projectEditMets.xhtml | 1 + 7 files changed, 72 insertions(+), 25 deletions(-) diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java index 6e31a64a32e..138fe59053f 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java @@ -141,8 +141,11 @@ public class Project extends BaseIndexedBean implements Comparable { @JoinColumn(name = "mediaView_audio_folder_id", foreignKey = @ForeignKey(name = "FK_project_mediaView_audio_folder_id")) private Folder audioMediaView; + /** + * Field to define the status of the audio media view waveform. + */ @Column(name = "mediaView_audio_waveform") - private Boolean audioMediaViewWaveform = true; + private Boolean audioMediaViewWaveform = false; /** * Folder with media to use for the video preview. @@ -555,10 +558,20 @@ public void setAudioMediaView(Folder audioMediaView) { this.audioMediaView = audioMediaView; } + /** + * Get the status of the audio media view waveform. + * + * @return True if is active + */ public boolean isAudioMediaViewWaveform() { return audioMediaViewWaveform; } + /** + * Set the status of the audio media view waveform. + * + * @param audioMediaViewWaveform True if is active + */ public void setAudioMediaViewWaveform(boolean audioMediaViewWaveform) { this.audioMediaViewWaveform = audioMediaViewWaveform; } diff --git a/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql index 58936814251..07cd539046f 100644 --- a/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql +++ b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql @@ -11,4 +11,4 @@ -- -- Migration: Add column for state of audio waveform in media view to project table. -ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 1 +ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 0 diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java index 9818611bfb6..9cb3266068c 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java @@ -228,6 +228,13 @@ public void onPageDrop() { } } + /** + * Check if audio media view waveform is activated in project. + */ + public boolean isAudioMediaViewWaveform() { + return dataEditor.getProcess().getProject().isAudioMediaViewWaveform(); + } + private boolean dragStripeIndexMatches(String dragId) { Matcher dragStripeImageMatcher = DRAG_STRIPE_IMAGE.matcher(dragId); Matcher dragUnstructuredMediaMatcher = DRAG_UNSTRUCTURED_MEDIA.matcher(dragId); diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index 278e19e3288..7f96f26f5a7 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3966,6 +3966,16 @@ footer { max-width: 300px; } +.loader { + width: 30px; + height: 30px; + color: white; + display: flex; + justify-content: center; + align-items: center; + padding: 10px; +} + /*---------------------------------------------------------------------- min 700px ----------------------------------------------------------------------*/ diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js index 71b11b61af0..058e7f6a6a0 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js @@ -16,10 +16,16 @@ let audios = document.querySelectorAll('audio.mediaPreviewItem'); let audio = audios[0]; audio.src = audio.currentSrc; +let loader = document.createElement("div"); +loader.innerHTML = '' +loader.classList.add('loader') +audio.parentNode.insertBefore(loader, audio); + let waveContainer = document.createElement("div"); waveContainer.setAttribute("id", "wave-container"); waveContainer.onclick = function(){wavesurfer.playPause()} waveContainer.style.width = "90%"; +waveContainer.style.display = "none"; audio.parentNode.insertBefore(waveContainer, audio); const wavesurfer = WaveSurfer.create({ @@ -32,6 +38,11 @@ const wavesurfer = WaveSurfer.create({ minPxPerSec: 0, }); +wavesurfer.on("ready", function () { + waveContainer.style.display = "block"; + loader.style.display = "none"; +}); + wavesurfer.once('decode', () => { let waveToolsContainer = document.getElementById("waveTools") const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') @@ -54,3 +65,4 @@ wavesurfer.once('decode', () => { wavesurfer.on("error", function (e) { console.warn(e); }); + diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index 2a9aa53866d..dbeb2071bb4 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -26,28 +26,30 @@ -
-
- + +
+
+ +
+
+ +
+
+ + + + +
-
- -
-
- - - - -
-
+ - - + + + + + From 399730e90083e9c72e8b8fd27f9adef2d44eb08a Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 4 Oct 2023 18:02:59 +0200 Subject: [PATCH 06/17] Update waveviewer implementation --- .../forms/dataeditor/GalleryPanel.java | 2 +- .../resources/messages/messages_de.properties | 4 + .../resources/messages/messages_en.properties | 6 +- .../webapp/WEB-INF/resources/css/kitodo.css | 2 +- .../resources/js/media_detail_audio.js | 68 ----------- .../js/media_detail_audio_waveform.js | 112 ++++++++++++++++++ .../WEB-INF/resources/js/metadata_editor.js | 8 ++ .../webapp/WEB-INF/resources/js/ol_custom.js | 4 + .../webapp/WEB-INF/resources/js/resize.js | 2 +- .../metadataEditor/logicalStructure.xhtml | 2 + .../partials/media-detail.xhtml | 24 ++-- .../metadataEditor/physicalStructure.xhtml | 4 +- 12 files changed, 154 insertions(+), 84 deletions(-) delete mode 100644 Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js create mode 100644 Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java index 9cb3266068c..614928f2921 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java @@ -836,7 +836,7 @@ private void selectMedia(String physicalDivisionOrder, String stripeIndex, Strin String scrollScripts = "scrollToSelectedTreeNode();scrollToSelectedPaginationRow();"; if (GalleryViewMode.PREVIEW.equals(galleryViewMode)) { - PrimeFaces.current().executeScript("checkScrollPosition();initializeImage();" + scrollScripts); + PrimeFaces.current().executeScript("checkScrollPosition();metadataEditor.gallery.mediaView.update();" + scrollScripts); } else { PrimeFaces.current().executeScript(scrollScripts); } diff --git a/Kitodo/src/main/resources/messages/messages_de.properties b/Kitodo/src/main/resources/messages/messages_de.properties index 760ae7488a3..d99a40973bb 100644 --- a/Kitodo/src/main/resources/messages/messages_de.properties +++ b/Kitodo/src/main/resources/messages/messages_de.properties @@ -9,6 +9,10 @@ # GPL3-License.txt file that was distributed with this source code. # AND=Enth\u00E4lt +audioWaveformJumpForwardFiveSeconds=5 Sekunden vorspringen +audioWaveformJumpForwardOneSecond=1 Sekunde vorspringen +audioWaveformJumpBackFiveSeconds=5 Sekunden zur\u00FCckspringen +audioWaveformJumpBackOneSecond=1 Sekunde zur\u00FCckspringen authorities=Berechtigungen authority=Berechtigung DMSExportByThread=Der Vorgang wird vom Taskmanager ins DMS exportiert\: diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index 0a9778894f9..c961b56308b 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -9,6 +9,10 @@ # GPL3-License.txt file that was distributed with this source code. # AND=Contains +audioWaveformJumpForwardFiveSeconds=Jump forward 5 seconds +audioWaveformJumpForwardOneSecond=Jump forward 1 second +audioWaveformJumpBackFiveSeconds=Jump back 5 seconds +audioWaveformJumpBackOneSecond=Jump back 5 second authorities=Authorities authority=Authority DMSExportByThread=The process is being exported into the DMS by the task manager\: @@ -1150,7 +1154,7 @@ userData=User data \u0026 settings userDN=User DN userDocumentationLink=Kitodo.Production 3.X user documentation userEdit.metadataEditorSettings = Metadata editor -userEdit.metadataEditorSettings.defaultGalleryView = Default gallery view +tooltip.importConfig.parentElementTypeHelp= Default gallery view userEdit.metadataEditorSettings.showCommentsByDefault = Show comments by default userEdit.metadataEditorSettings.showPaginationByDefault = Show pagination by default userInstruction=User instructions diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index 7f96f26f5a7..0bd634f50e6 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3062,7 +3062,7 @@ Column content #imagePreviewForm\:mediaDetail { display: flex; - width: 100%; + width: calc(100% - 100px); align-items: center; justify-content: center; flex-direction: column; diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js deleted file mode 100644 index 058e7f6a6a0..00000000000 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * (c) Kitodo. Key to digital objects e. V. - * - * This file is part of the Kitodo project. - * - * It is licensed under GNU General Public License version 3 or later. - * - * For the full copyright and license information, please read the - * GPL3-License.txt file that was distributed with this source code. - */ - -import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf' - -let audios = document.querySelectorAll('audio.mediaPreviewItem'); - -let audio = audios[0]; -audio.src = audio.currentSrc; - -let loader = document.createElement("div"); -loader.innerHTML = '' -loader.classList.add('loader') -audio.parentNode.insertBefore(loader, audio); - -let waveContainer = document.createElement("div"); -waveContainer.setAttribute("id", "wave-container"); -waveContainer.onclick = function(){wavesurfer.playPause()} -waveContainer.style.width = "90%"; -waveContainer.style.display = "none"; -audio.parentNode.insertBefore(waveContainer, audio); - -const wavesurfer = WaveSurfer.create({ - container: document.getElementById(waveContainer.getAttribute("id")), - height: 100, - waveColor: "#f3f3f3", - progressColor: "#ff4e00", - cursorColor: "#ffffff", - media: audio, - minPxPerSec: 0, -}); - -wavesurfer.on("ready", function () { - waveContainer.style.display = "block"; - loader.style.display = "none"; -}); - -wavesurfer.once('decode', () => { - let waveToolsContainer = document.getElementById("waveTools") - const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') - - waveToolsSlider.addEventListener('input', (e) => { - const minPxPerSec = e.target.valueAsNumber - wavesurfer.zoom(minPxPerSec) - }) - - waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => { - input.onchange = (e) => { - wavesurfer.setOptions({ - [input.value]: e.target.checked, - }) - } - }) -}) - - -wavesurfer.on("error", function (e) { - console.warn(e); -}); - diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js new file mode 100644 index 00000000000..dd8d71bc195 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js @@ -0,0 +1,112 @@ +/** + * (c) Kitodo. Key to digital objects e. V. + * + * This file is part of the Kitodo project. + * + * It is licensed under GNU General Public License version 3 or later. + * + * For the full copyright and license information, please read the + * GPL3-License.txt file that was distributed with this source code. + */ + +import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf' + +class AudioWaveform { + #audioElement; + #wavesurfer; + + constructor() { + this.init(); + } + + init() { + this.#audioElement = document.querySelector('audio.mediaPreviewItem') + if (this.#audioElement.getAttribute("data-audio-waveform") != "initialized") { + this.#audioElement.setAttribute("data-audio-waveform", "initialized") + this.#build(); + + /* + console.log("add can play handler"); + this.#audioElement && this.#audioElement.addEventListener("canplay", () => { + console.log("handle can play once"); + + }, {once: true});*/ + } + } + + #build() { + let self = this + this.#audioElement.src = this.#audioElement.currentSrc; + + let loader = document.createElement("div"); + loader.innerHTML = '' + loader.classList.add('loader') + this.#audioElement.parentNode.insertBefore(loader, this.#audioElement); + + let waveContainer = document.createElement("div"); + waveContainer.setAttribute("id", "wave-container"); + waveContainer.onclick = function () { + self.#wavesurfer.playPause() + } + waveContainer.style.width = "90%"; + waveContainer.style.display = "none"; + this.#audioElement.parentNode.insertBefore(waveContainer, this.#audioElement); + + this.#wavesurfer = WaveSurfer.create({ + container: document.getElementById(waveContainer.getAttribute("id")), + height: 100, + waveColor: "#f3f3f3", + progressColor: "#ff4e00", + cursorColor: "#ffffff", + media: this.#audioElement, + minPxPerSec: 0, + }); + + console.log("add ready handler"); + this.#wavesurfer.on("ready", function () { + console.log("run ready handler"); + waveContainer.style.display = "block"; + loader.style.display = "none"; + + let waveToolsContainer = document.getElementById("waveTools") + const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') + + waveToolsSlider.addEventListener('input', (e) => { + const minPxPerSec = e.target.valueAsNumber + this.#wavesurfer.zoom(minPxPerSec) + }) + + waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => { + input.onchange = (e) => { + this.#wavesurfer.setOptions({ + [input.value]: e.target.checked, + }) + } + }) + const jumpButtons = document.getElementsByClassName("audio-waveform-jump-button"); + Array.from(jumpButtons).forEach(function (jumpButton) { + jumpButton.addEventListener('click', function (event) { + event.stopPropagation(); + let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-jump-seconds")); + this.#wavesurfer.setTime(this.#wavesurfer.getCurrentTime() + jumpSeconds) + }); + }); + }); + + this.#wavesurfer.on("error", function (e) { + console.error(e); + }); + } + +} + +const audioWaveform= new AudioWaveform() +console.log("first initialisation"); +let timeout; +document.addEventListener("kitodo-metadataditor-mediaview-update", function () { + console.log("update media view"); + clearTimeout(timeout); + timeout = setTimeout(function() { + audioWaveform.init(); + }, 2000) +}); diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js index 476ad6b0009..284b37beaff 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js @@ -433,6 +433,14 @@ metadataEditor.gallery = { } }, + mediaView: { + updateEventName : "kitodo-metadataditor-mediaview-update", + update(){ + console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1]) + document.dispatchEvent(new Event(this.updateEventName)); + } + }, + /** * Event handlers methods related to gallery stripes */ diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js index 049d0f7da75..7c4f0e49c08 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js @@ -252,3 +252,7 @@ $('#thirdColumnWrapper').on('resize', function () { $(document).ready(function () { initializeImage(); }); + +document.addEventListener("metadataEditor.gallery.mediaView.updateEventName", function (event) { + initializeImage(); +}); diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js index 26a1f5f33b3..6df50a42da2 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js @@ -522,7 +522,7 @@ function updateMetadataEditorView(showMetadataColumn) { } expandThirdColumn(); scrollToSelectedThumbnail(); - initializeImage(); + metadataEditor.gallery.mediaView.update(); scrollToSelectedTreeNode(); scrollToSelectedPaginationRow(); } diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/logicalStructure.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/logicalStructure.xhtml index f1d3404c44b..8a2a5f70484 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/logicalStructure.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/logicalStructure.xhtml @@ -73,6 +73,7 @@ oncomplete="scrollToSelectedThumbnail(); scrollToSelectedPaginationRow(); changeToMapView(); + metadataEditor.gallery.mediaView.update(); expandMetadata('logical-metadata-tab');" update="galleryHeadingWrapper imagePreviewForm:mediaDetail @@ -86,6 +87,7 @@ oncomplete="scrollToSelectedThumbnail(); scrollToSelectedPaginationRow(); changeToMapView(); + metadataEditor.gallery.mediaView.update(); PF('contextMenuLogicalTree').show(currentEvent)" update="@(.stripe) @(.thumbnail) diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index dbeb2071bb4..fc4a513111d 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -29,24 +29,28 @@
- +
- - + - + - + + styleClass="audio-waveform-jump-button secondary" + a:data-audio-waveform-jump-seconds="5"/>
@@ -65,7 +69,7 @@ - +
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml index 337879d9e06..4837d90324a 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml @@ -34,7 +34,7 @@ listener="#{DataEditorForm.structurePanel.treePhysicalSelect}" oncomplete="scrollToSelectedThumbnail(); scrollToSelectedPaginationRow(); - initializeImage(); + metadataEditor.gallery.mediaView.update(); expandMetadata('physical-metadata-tab');" update="galleryHeadingWrapper imagePreviewForm:mediaDetail @@ -42,7 +42,7 @@ { - console.log("handle can play once"); + clearTimeout(this.#buildTimeout ); + self.#buildTimeout = setTimeout(function() { + self.#build(); + }, 500) + }, {once: true}); + + this.#loader = document.createElement("div"); + this.#loader.innerHTML = '' + this.#loader.classList.add('loader') + this.#audioElement.parentNode.insertBefore(this.#loader, this.#audioElement); - }, {once: true});*/ } } @@ -38,10 +47,8 @@ class AudioWaveform { let self = this this.#audioElement.src = this.#audioElement.currentSrc; - let loader = document.createElement("div"); - loader.innerHTML = '' - loader.classList.add('loader') - this.#audioElement.parentNode.insertBefore(loader, this.#audioElement); + const urlParams = new URLSearchParams(this.#audioElement.src); + let mediaId = urlParams.get('mediaId') let waveContainer = document.createElement("div"); waveContainer.setAttribute("id", "wave-container"); @@ -60,25 +67,32 @@ class AudioWaveform { cursorColor: "#ffffff", media: this.#audioElement, minPxPerSec: 0, + peaks: this.#peaksCache[mediaId] }); console.log("add ready handler"); + + // cache peaks after when audio has been decoded + this.#wavesurfer.on("decode", function () { + self.#peaksCache[mediaId] = self.#wavesurfer.getDecodedData().getChannelData(0) + }); + this.#wavesurfer.on("ready", function () { console.log("run ready handler"); waveContainer.style.display = "block"; - loader.style.display = "none"; + self.#loader.style.display = "none"; let waveToolsContainer = document.getElementById("waveTools") const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') waveToolsSlider.addEventListener('input', (e) => { const minPxPerSec = e.target.valueAsNumber - this.#wavesurfer.zoom(minPxPerSec) + self.#wavesurfer.zoom(minPxPerSec) }) waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => { input.onchange = (e) => { - this.#wavesurfer.setOptions({ + self.#wavesurfer.setOptions({ [input.value]: e.target.checked, }) } @@ -88,7 +102,7 @@ class AudioWaveform { jumpButton.addEventListener('click', function (event) { event.stopPropagation(); let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-jump-seconds")); - this.#wavesurfer.setTime(this.#wavesurfer.getCurrentTime() + jumpSeconds) + self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds) }); }); }); @@ -100,13 +114,10 @@ class AudioWaveform { } -const audioWaveform= new AudioWaveform() console.log("first initialisation"); -let timeout; +const audioWaveform= new AudioWaveform() + document.addEventListener("kitodo-metadataditor-mediaview-update", function () { console.log("update media view"); - clearTimeout(timeout); - timeout = setTimeout(function() { - audioWaveform.init(); - }, 2000) + audioWaveform.init(); }); From 6b5bf50f98eae4dc860f126d1b2f69414dac4c9a Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Thu, 5 Oct 2023 17:12:17 +0200 Subject: [PATCH 08/17] improvements selecting media view and comments in media audio waveform --- .../js/media_detail_audio_waveform.js | 26 +++++++++---------- .../metadataEditor/dialogs/pagination.xhtml | 2 +- .../includes/metadataEditor/gallery.xhtml | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js index ab7ca7d4119..daf2c2d9fa0 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js @@ -25,28 +25,33 @@ class AudioWaveform { init() { let self = this this.#audioElement = document.querySelector('audio.mediaPreviewItem') - if (this.#audioElement.getAttribute("data-audio-waveform") != "initialized") { + if (this.#audioElement && this.#audioElement.getAttribute("data-audio-waveform") != "initialized") { this.#audioElement.setAttribute("data-audio-waveform", "initialized") + // add a loader to visualize loading process + this.#loader = document.createElement("div"); + this.#loader.innerHTML = '' + this.#loader.classList.add('loader') + this.#audioElement.parentNode.insertBefore(this.#loader, this.#audioElement); + + // when the user agent can play the media this.#audioElement && this.#audioElement.addEventListener("canplay", () => { - clearTimeout(this.#buildTimeout ); + // Prevent browser crashes during audio decoding when multiple rapid clicks, such as double-clicking, occur. + clearTimeout(this.#buildTimeout); self.#buildTimeout = setTimeout(function() { self.#build(); }, 500) }, {once: true}); - this.#loader = document.createElement("div"); - this.#loader.innerHTML = '' - this.#loader.classList.add('loader') - this.#audioElement.parentNode.insertBefore(this.#loader, this.#audioElement); - } } #build() { let self = this + // wavesurfer uses the 'src' attribute of the audio element, and we add this attribute based on the browser's current source selection this.#audioElement.src = this.#audioElement.currentSrc; + // get the media id from the source parameter const urlParams = new URLSearchParams(this.#audioElement.src); let mediaId = urlParams.get('mediaId') @@ -70,15 +75,12 @@ class AudioWaveform { peaks: this.#peaksCache[mediaId] }); - console.log("add ready handler"); - - // cache peaks after when audio has been decoded this.#wavesurfer.on("decode", function () { + // cache peaks after when audio has been decoded self.#peaksCache[mediaId] = self.#wavesurfer.getDecodedData().getChannelData(0) }); this.#wavesurfer.on("ready", function () { - console.log("run ready handler"); waveContainer.style.display = "block"; self.#loader.style.display = "none"; @@ -114,10 +116,8 @@ class AudioWaveform { } -console.log("first initialisation"); const audioWaveform= new AudioWaveform() document.addEventListener("kitodo-metadataditor-mediaview-update", function () { - console.log("update media view"); audioWaveform.init(); }); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/pagination.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/pagination.xhtml index d0bd9ab3245..451650af560 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/pagination.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/pagination.xhtml @@ -47,7 +47,7 @@ showCheckbox="true">
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml index 7c089dc4879..963816c6116 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml @@ -65,7 +65,7 @@ From 178d7ac84146263cc48e9880b85adcb3a683c319 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Thu, 5 Oct 2023 17:26:48 +0200 Subject: [PATCH 09/17] Remove initializeImage handling to focus on behavoir of waveform --- .../org/kitodo/production/forms/dataeditor/GalleryPanel.java | 2 +- Kitodo/src/main/resources/messages/messages_en.properties | 2 +- .../src/main/webapp/WEB-INF/resources/js/metadata_editor.js | 1 - Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js | 4 ---- Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js | 1 + .../templates/includes/metadataEditor/physicalStructure.xhtml | 3 ++- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java index 614928f2921..e929242699d 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java @@ -836,7 +836,7 @@ private void selectMedia(String physicalDivisionOrder, String stripeIndex, Strin String scrollScripts = "scrollToSelectedTreeNode();scrollToSelectedPaginationRow();"; if (GalleryViewMode.PREVIEW.equals(galleryViewMode)) { - PrimeFaces.current().executeScript("checkScrollPosition();metadataEditor.gallery.mediaView.update();" + scrollScripts); + PrimeFaces.current().executeScript("checkScrollPosition();initializeImage();metadataEditor.gallery.mediaView.update();" + scrollScripts); } else { PrimeFaces.current().executeScript(scrollScripts); } diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index c961b56308b..6a323a8cc18 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -1154,7 +1154,7 @@ userData=User data \u0026 settings userDN=User DN userDocumentationLink=Kitodo.Production 3.X user documentation userEdit.metadataEditorSettings = Metadata editor -tooltip.importConfig.parentElementTypeHelp= Default gallery view +userEdit.metadataEditorSettings.defaultGalleryView= Default gallery view userEdit.metadataEditorSettings.showCommentsByDefault = Show comments by default userEdit.metadataEditorSettings.showPaginationByDefault = Show pagination by default userInstruction=User instructions diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js index 284b37beaff..e0922870cd9 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadata_editor.js @@ -436,7 +436,6 @@ metadataEditor.gallery = { mediaView: { updateEventName : "kitodo-metadataditor-mediaview-update", update(){ - console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1]) document.dispatchEvent(new Event(this.updateEventName)); } }, diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js index 7c4f0e49c08..049d0f7da75 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js @@ -252,7 +252,3 @@ $('#thirdColumnWrapper').on('resize', function () { $(document).ready(function () { initializeImage(); }); - -document.addEventListener("metadataEditor.gallery.mediaView.updateEventName", function (event) { - initializeImage(); -}); diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js index 6df50a42da2..4bdfc1ce713 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js @@ -522,6 +522,7 @@ function updateMetadataEditorView(showMetadataColumn) { } expandThirdColumn(); scrollToSelectedThumbnail(); + initializeImage(); metadataEditor.gallery.mediaView.update(); scrollToSelectedTreeNode(); scrollToSelectedPaginationRow(); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml index 4837d90324a..0545befe768 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/physicalStructure.xhtml @@ -34,6 +34,7 @@ listener="#{DataEditorForm.structurePanel.treePhysicalSelect}" oncomplete="scrollToSelectedThumbnail(); scrollToSelectedPaginationRow(); + initializeImage(); metadataEditor.gallery.mediaView.update(); expandMetadata('physical-metadata-tab');" update="galleryHeadingWrapper @@ -42,7 +43,7 @@ { @@ -99,11 +99,11 @@ class AudioWaveform { }) } }) - const jumpButtons = document.getElementsByClassName("audio-waveform-jump-button"); + const jumpButtons = document.getElementsByClassName("audio-waveform-tools-jump-button"); Array.from(jumpButtons).forEach(function (jumpButton) { jumpButton.addEventListener('click', function (event) { event.stopPropagation(); - let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-jump-seconds")); + let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-tools-jump-seconds")); self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds) }); }); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index fc4a513111d..f893fc0be09 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -27,30 +27,30 @@ rendered="#{mediaProvider.hasMediaViewVariant(selectedGalleryMediaContent) and (fn:startsWith(selectedGalleryMediaContent.mediaViewMimeType, 'video') or fn:startsWith(selectedGalleryMediaContent.mediaViewMimeType, 'audio'))}"> -
+
- +
- +
- - + - + - + + styleClass="audio-waveform-tools-jump-button secondary" + a:data-audio-waveform-tools-jump-seconds="5"/>
From 623b76d2a1ae497f42531e5244213d604cabf39e Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Thu, 5 Oct 2023 18:42:25 +0200 Subject: [PATCH 11/17] Fix checkstyle problem --- .../org/kitodo/production/forms/dataeditor/GalleryPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java index e929242699d..2d93f89f83d 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java @@ -836,7 +836,8 @@ private void selectMedia(String physicalDivisionOrder, String stripeIndex, Strin String scrollScripts = "scrollToSelectedTreeNode();scrollToSelectedPaginationRow();"; if (GalleryViewMode.PREVIEW.equals(galleryViewMode)) { - PrimeFaces.current().executeScript("checkScrollPosition();initializeImage();metadataEditor.gallery.mediaView.update();" + scrollScripts); + PrimeFaces.current().executeScript( + "checkScrollPosition();initializeImage();metadataEditor.gallery.mediaView.update();" + scrollScripts); } else { PrimeFaces.current().executeScript(scrollScripts); } From e180146dd27b741d0b59a62f6ce23bd283dd7c9e Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Fri, 6 Oct 2023 11:24:30 +0200 Subject: [PATCH 12/17] Create LICENSE --- .../resources/js/libs/wavesurfer/LICENSE | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/LICENSE diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/LICENSE b/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/LICENSE new file mode 100644 index 00000000000..88998ae68f3 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/libs/wavesurfer/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2012-2023, katspaugh and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From db44aca6e4a303ce01c167d0689664be51899524 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Fri, 6 Oct 2023 16:20:41 +0200 Subject: [PATCH 13/17] Fix codacy validation errors --- .../webapp/WEB-INF/resources/css/kitodo.css | 2 +- .../js/media_detail_audio_waveform.js | 42 +++++++++---------- .../webapp/WEB-INF/resources/js/resize.js | 2 +- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index 8886729cbd3..5d6f8d851ed 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3084,7 +3084,7 @@ Column content } #imagePreviewForm\:mediaDetail #audioWaveformTools > div > button:first-child { - margin-left: 0px; + margin-left: 0; } #imagePreviewForm\:mediaDetail #audioWaveformTools > div > button { diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js index 4c1f60a701c..278a5eef5e3 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js @@ -9,7 +9,7 @@ * GPL3-License.txt file that was distributed with this source code. */ -import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf' +import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf'; class AudioWaveform { #audioElement; @@ -23,15 +23,15 @@ class AudioWaveform { } init() { - let self = this - this.#audioElement = document.querySelector('audio.mediaPreviewItem') - if (this.#audioElement && this.#audioElement.getAttribute("data-audio-waveform") != "initialized") { - this.#audioElement.setAttribute("data-audio-waveform", "initialized") + let self = this; + this.#audioElement = document.querySelector('audio.mediaPreviewItem'); + if (this.#audioElement && this.#audioElement.getAttribute("data-audio-waveform") !== "initialized") { + this.#audioElement.setAttribute("data-audio-waveform", "initialized"); // add a loader to visualize loading process this.#loader = document.createElement("div"); - this.#loader.innerHTML = '' - this.#loader.classList.add('loader') + this.#loader.innerHTML = ''; + this.#loader.classList.add('loader'); this.#audioElement.parentNode.insertBefore(this.#loader, this.#audioElement); // when the user agent can play the media @@ -40,7 +40,7 @@ class AudioWaveform { clearTimeout(this.#buildTimeout); self.#buildTimeout = setTimeout(function() { self.#build(); - }, 500) + }, 500); }, {once: true}); } @@ -53,13 +53,13 @@ class AudioWaveform { // get the media id from the source parameter const urlParams = new URLSearchParams(this.#audioElement.src); - let mediaId = urlParams.get('mediaId') + let mediaId = urlParams.get('mediaId'); let waveContainer = document.createElement("div"); waveContainer.setAttribute("id", "wave-container"); waveContainer.onclick = function () { - self.#wavesurfer.playPause() - } + self.#wavesurfer.playPause(); + }; waveContainer.style.width = "90%"; waveContainer.style.display = "none"; this.#audioElement.parentNode.insertBefore(waveContainer, this.#audioElement); @@ -77,20 +77,20 @@ class AudioWaveform { this.#wavesurfer.on("decode", function () { // cache peaks after when audio has been decoded - self.#peaksCache[mediaId] = self.#wavesurfer.getDecodedData().getChannelData(0) + self.#peaksCache[mediaId] = self.#wavesurfer.getDecodedData().getChannelData(0); }); this.#wavesurfer.on("ready", function () { waveContainer.style.display = "block"; self.#loader.style.display = "none"; - let waveToolsContainer = document.getElementById("audioWaveformTools") - const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]') + let waveToolsContainer = document.getElementById("audioWaveformTools"); + const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]'); waveToolsSlider.addEventListener('input', (e) => { - const minPxPerSec = e.target.valueAsNumber - self.#wavesurfer.zoom(minPxPerSec) - }) + const minPxPerSec = e.target.valueAsNumber; + self.#wavesurfer.zoom(minPxPerSec); + }); waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => { input.onchange = (e) => { @@ -104,19 +104,15 @@ class AudioWaveform { jumpButton.addEventListener('click', function (event) { event.stopPropagation(); let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-tools-jump-seconds")); - self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds) + self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds); }); }); }); - - this.#wavesurfer.on("error", function (e) { - console.error(e); - }); } } -const audioWaveform= new AudioWaveform() +const audioWaveform= new AudioWaveform(); document.addEventListener("kitodo-metadataditor-mediaview-update", function () { audioWaveform.init(); diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js index 4bdfc1ce713..208bb40dcc1 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js @@ -8,7 +8,7 @@ * For the full copyright and license information, please read the * GPL3-License.txt file that was distributed with this source code. */ -/* globals PF */ +/* globals PF, metadataEditor */ // jshint unused:false var SEPARATOR_WIDTH = 3; From 1c8296c6b945aab587c5bbeb3a3ef42262fabf92 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 1 Nov 2023 16:07:52 +0100 Subject: [PATCH 14/17] Update Kitodo/src/main/resources/messages/messages_en.properties Co-authored-by: Arved Solth --- Kitodo/src/main/resources/messages/messages_en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index 83203d8d486..18438f8f481 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -13,7 +13,7 @@ audioWaveformToolsCenteredCursor=Centered cursor audioWaveformToolsJumpForwardFiveSeconds=Jump forward 5 seconds audioWaveformToolsJumpForwardOneSecond=Jump forward 1 second audioWaveformToolsJumpBackFiveSeconds=Jump back 5 seconds -audioWaveformToolsJumpBackOneSecond=Jump back 5 second +audioWaveformToolsJumpBackOneSecond=Jump back 1 second audioWaveformToolsZoom=Zoom authorities=Authorities authority=Authority From 1dcf18a2656b49824b99c3e6d46b5dc0506db025 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 1 Nov 2023 16:08:04 +0100 Subject: [PATCH 15/17] Update Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css Co-authored-by: Arved Solth --- Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css | 1 - 1 file changed, 1 deletion(-) diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index 5d6f8d851ed..6a01c68d285 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -3091,7 +3091,6 @@ Column content margin-left: 10px; } - #imagePreviewForm .mediaListIconItem { height: 100%; width: 100%; From 5a9279f5cb4236db00ce2c79c209cdf804c9a602 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 1 Nov 2023 16:17:08 +0100 Subject: [PATCH 16/17] Add missing semicolon --- .../V2_125__Add_audio_media_view_waveform_to_project.sql | 2 +- .../WEB-INF/resources/js/media_detail_audio_waveform.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql index 07cd539046f..3918267acbf 100644 --- a/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql +++ b/Kitodo-DataManagement/src/main/resources/db/migration/V2_125__Add_audio_media_view_waveform_to_project.sql @@ -11,4 +11,4 @@ -- -- Migration: Add column for state of audio waveform in media view to project table. -ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 0 +ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 0; diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js index 278a5eef5e3..82727552b99 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js @@ -47,7 +47,7 @@ class AudioWaveform { } #build() { - let self = this + let self = this; // wavesurfer uses the 'src' attribute of the audio element, and we add this attribute based on the browser's current source selection this.#audioElement.src = this.#audioElement.currentSrc; @@ -96,9 +96,9 @@ class AudioWaveform { input.onchange = (e) => { self.#wavesurfer.setOptions({ [input.value]: e.target.checked, - }) + }); } - }) + }); const jumpButtons = document.getElementsByClassName("audio-waveform-tools-jump-button"); Array.from(jumpButtons).forEach(function (jumpButton) { jumpButton.addEventListener('click', function (event) { From a221260c5a405c7bdc139dadb666720e0c1015cf Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Wed, 1 Nov 2023 16:35:34 +0100 Subject: [PATCH 17/17] Set button type and remove stopping of propagation --- .../WEB-INF/resources/js/media_detail_audio_waveform.js | 3 +-- .../includes/metadataEditor/partials/media-detail.xhtml | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js index 82727552b99..72360c1cd27 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/media_detail_audio_waveform.js @@ -101,8 +101,7 @@ class AudioWaveform { }); const jumpButtons = document.getElementsByClassName("audio-waveform-tools-jump-button"); Array.from(jumpButtons).forEach(function (jumpButton) { - jumpButton.addEventListener('click', function (event) { - event.stopPropagation(); + jumpButton.addEventListener('click', function () { let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-tools-jump-seconds")); self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds); }); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml index f893fc0be09..ea62892ec87 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/partials/media-detail.xhtml @@ -36,18 +36,22 @@