From: Mathieu Baudier Date: Mon, 19 Jul 2021 05:58:24 +0000 (+0200) Subject: Support embedding videos. X-Git-Tag: argeo-suite-2.3.1~17 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=cc5f3a7a328e0487d0b06dd4e99b232a2e551461 Support embedding videos. --- diff --git a/publishing/org.argeo.publishing.ui/OSGI-INF/l10n/bundle.properties b/publishing/org.argeo.publishing.ui/OSGI-INF/l10n/bundle.properties index 54f5ce8..302f555 100644 --- a/publishing/org.argeo.publishing.ui/OSGI-INF/l10n/bundle.properties +++ b/publishing/org.argeo.publishing.ui/OSGI-INF/l10n/bundle.properties @@ -7,5 +7,6 @@ insertParagraph=insert paragraph deleteParagraph=delete paragraph deleteSection=delete section -insertMedia=insert media +insertPicture=insert picture +insertVideo=insert video deleteMedia=delete media \ No newline at end of file diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkMsg.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkMsg.java index 7f82178..8e87231 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkMsg.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkMsg.java @@ -8,7 +8,7 @@ public enum DbkMsg implements Localized { // section, deleteSection, // - media, deleteMedia, insertMedia, insertParagraph, + media, deleteMedia, insertPicture, insertVideo, insertParagraph, // ; } diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkType.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkType.java index cf3b293..005d165 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkType.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkType.java @@ -8,7 +8,7 @@ public enum DbkType implements JcrName { // info, title, para, // - mediaobject, imageobject, imagedata, videoobject, videodata, + mediaobject, imageobject, imagedata, videoobject, videodata, caption, // link, // diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkUtils.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkUtils.java index 6db73d0..15d52f3 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkUtils.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkUtils.java @@ -11,7 +11,9 @@ import java.nio.file.Path; import javax.jcr.ImportUUIDBehavior; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; +import javax.jcr.ValueFormatException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -168,6 +170,46 @@ public class DbkUtils { } } + public static Node insertVideoAfter(Node sibling) { + try { + + Node parent = sibling.getParent(); + Node mediaNode = addDbk(parent, DbkType.mediaobject); + // TODO optimise? + parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", + sibling.getName() + "[" + sibling.getIndex() + "]"); + parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", + mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); + + Node videoNode = addDbk(mediaNode, DbkType.videoobject); + Node videoDataNode = addDbk(videoNode, DbkType.videodata); + return mediaNode; + } catch (RepositoryException e) { + throw new JcrException("Cannot insert empty image after " + sibling, e); + } + } + + public static String getMediaFileref(Node node) { + try { + Node mediadata; + if (node.hasNode(DbkType.imageobject.get())) { + mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get()); + } else if (node.hasNode(DbkType.videoobject.get())) { + mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get()); + } else { + throw new IllegalArgumentException("Fileref not found in " + node); + } + + if (mediadata.hasProperty(DbkAttr.fileref.name())) { + return mediadata.getProperty(DbkAttr.fileref.name()).getString(); + } else { + return null; + } + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve file ref from " + node, e); + } + } + public static void exportXml(Node node, OutputStream out) throws IOException { try { node.getSession().exportDocumentView(node.getPath(), out, false, false); diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java index 2249ff0..cde5c7f 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java @@ -221,24 +221,18 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke protected DbkVideo newVideo(TextSection parent, Node node) { try { -// node.getSession(); -// Composite wrapper = new Composite(parent, SWT.NONE); -// new Label(wrapper,SWT.NONE).setText("TEST"); - DbkVideo video = new DbkVideo(parent, SWT.BORDER, node); + DbkVideo video = new DbkVideo(parent,getCmsEditable().canEdit()?SWT.NONE : SWT.READ_ONLY, node); GridData gd; if (maxMediaWidth != null) { gd = new GridData(SWT.CENTER, SWT.FILL, false, false); // TODO, manage size gd.widthHint = maxMediaWidth; gd.heightHint = (int) (gd.heightHint * 0.5625); -// img.setPreferredSize(new Point(maxMediaWidth, 0)); } else { gd = new GridData(SWT.CENTER, SWT.FILL, false, false); - gd.widthHint = video.getWidth(); - gd.heightHint = video.getHeight(); -// gd = new GridData(video.getWidth(),video.getHeight()); +// gd.widthHint = video.getWidth(); +// gd.heightHint = video.getHeight(); } -// wrapper.setLayoutData(gd); video.setLayoutData(gd); updateContent(video); return null; @@ -305,7 +299,8 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke paragraph.setText(textInterpreter.readSimpleHtml(partNode)); } else if (part instanceof DbkImg) { DbkImg editableImage = (DbkImg) part; - //imageManager.load(partNode, part.getControl(), editableImage.getPreferredImageSize()); + // imageManager.load(partNode, part.getControl(), + // editableImage.getPreferredImageSize()); } else if (part instanceof DbkVideo) { DbkVideo video = (DbkVideo) part; video.load(part.getControl()); diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkContextMenu.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkContextMenu.java index 57c8356..a07a1cb 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkContextMenu.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkContextMenu.java @@ -58,12 +58,12 @@ class DbkContextMenu { if (editablePart instanceof Paragraph) { Paragraph paragraph = (Paragraph) editablePart; deletePartB(parent, DbkMsg.deleteParagraph.lead(), paragraph); - insertMediaB(parent, DbkMsg.insertMedia.lead(), paragraph); + insertMediaB(parent, paragraph); } else if (editablePart instanceof Img) { Img img = (Img) editablePart; deletePartB(parent, DbkMsg.deleteMedia.lead(), img); - insertMediaB(parent, DbkMsg.insertMedia.lead(), img); + insertMediaB(parent, img); insertParagraphB(parent, DbkMsg.insertParagraph.lead(), img); } else if (editablePart instanceof DocBookSectionTitle) { @@ -77,7 +77,7 @@ class DbkContextMenu { hide(); }); } - insertMediaB(parent, DbkMsg.insertMedia.lead(), sectionTitle.getSection(), sectionTitle); + insertMediaB(parent, sectionTitle.getSection(), sectionTitle); } StyledToolMouseListener stml = new StyledToolMouseListener(editablePart); @@ -128,19 +128,27 @@ class DbkContextMenu { shell.setVisible(false); } - protected void insertMediaB(Composite parent, String msg, SectionPart sectionPart) { - insertMediaB(parent, msg, sectionPart.getSection(), sectionPart); + protected void insertMediaB(Composite parent, SectionPart sectionPart) { + insertMediaB(parent, sectionPart.getSection(), sectionPart); } - protected void insertMediaB(Composite parent, String msg, Section section, NodePart nodePart) { - Label insertMediaB = new Label(parent, SWT.NONE); - insertMediaB.setText(DbkMsg.insertMedia.lead()); - insertMediaB.addMouseListener((MouseDown) (e) -> { + protected void insertMediaB(Composite parent, Section section, NodePart nodePart) { + Label insertPictureB = new Label(parent, SWT.NONE); + insertPictureB.setText(DbkMsg.insertPicture.lead()); + insertPictureB.addMouseListener((MouseDown) (e) -> { Node newNode = DbkUtils.insertImageAfter(nodePart.getNode()); Jcr.save(newNode); textViewer.insertPart(section, newNode); hide(); }); + Label insertVideoB = new Label(parent, SWT.NONE); + insertVideoB.setText(DbkMsg.insertVideo.lead()); + insertVideoB.addMouseListener((MouseDown) (e) -> { + Node newNode = DbkUtils.insertVideoAfter(nodePart.getNode()); + Jcr.save(newNode); + textViewer.insertPart(section, newNode); + hide(); + }); } diff --git a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkVideo.java b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkVideo.java index 6ad7ba0..af2ee84 100644 --- a/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkVideo.java +++ b/publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkVideo.java @@ -1,10 +1,13 @@ package org.argeo.docbook.ui; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; + import javax.jcr.Item; import javax.jcr.Node; -import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; -import javax.jcr.ValueFormatException; import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.cms.ui.viewers.NodePart; @@ -13,12 +16,19 @@ import org.argeo.cms.ui.viewers.SectionPart; import org.argeo.cms.ui.widgets.StyledControl; import org.argeo.docbook.DbkAttr; import org.argeo.docbook.DbkType; +import org.argeo.docbook.DbkUtils; +import org.argeo.eclipse.ui.Selected; import org.argeo.jcr.JcrException; +import org.argeo.naming.NamingUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; public class DbkVideo extends StyledControl implements SectionPart, NodePart { private static final long serialVersionUID = -8753232181570351880L; @@ -27,43 +37,137 @@ public class DbkVideo extends StyledControl implements SectionPart, NodePart { private int width = 640; private int height = 360; + private boolean editable; + public DbkVideo(Composite parent, int style, Node node) { this(Section.findSection(parent), parent, style, node); } DbkVideo(Section section, Composite parent, int style, Node node) { super(parent, style, node); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); this.section = section; setStyle(DbkType.videoobject.name()); } @Override protected Control createControl(Composite box, String style) { - Browser browser = new Browser(box, SWT.NONE); + Node mediaobject = getNode(); + Composite wrapper = new Composite(box, SWT.NONE); + wrapper.setLayout(CmsUiUtils.noSpaceGridLayout()); + + Composite browserC = new Composite(wrapper, SWT.NONE); + browserC.setLayout(CmsUiUtils.noSpaceGridLayout()); +// wrapper.setLayoutData(CmsUiUtils.fillAll()); + Browser browser = new Browser(browserC, SWT.NONE); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = getWidth(); gd.heightHint = getHeight(); - browser.setLayoutData(gd); + browserC.setLayoutData(gd); + + if (editable) { + Composite editor = new Composite(wrapper, SWT.NONE); + editor.setLayout(new GridLayout(3, false)); + editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + String fileref = DbkUtils.getMediaFileref(mediaobject); + Text text = new Text(editor, SWT.SINGLE); + if (fileref != null) + text.setText(fileref); + else + text.setMessage("Embed URL of the video."); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + Button updateB = new Button(editor, SWT.FLAT); + updateB.setText("Update"); + updateB.addSelectionListener(new Selected() { + + @Override + public void widgetSelected(SelectionEvent e) { + try { + Node videodata = mediaobject.getNode(DbkType.videoobject.get()) + .getNode(DbkType.videodata.get()); + String txt = text.getText(); + URI uri; + try { + uri = new URI(txt); + } catch (URISyntaxException e1) { + text.setText(""); + text.setMessage("Invalid URL"); + return; + } + + // transform YouTube watch URL in embed + String youTubeVideoId = null; + if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) { + if ("/watch".equals(uri.getPath())) { + Map> map = NamingUtils.queryToMap(uri); + youTubeVideoId = map.get("v").get(0); + } + } else if ("youtu.be".equals(uri.getHost())) { + youTubeVideoId = uri.getPath().substring(1); + } + if (youTubeVideoId != null) { + try { + uri = new URI("https://www.youtube.com/embed/" + youTubeVideoId); + text.setText(uri.toString()); + } catch (URISyntaxException e1) { + throw new IllegalStateException(e1); + } + } + + videodata.setProperty(DbkAttr.fileref.name(), uri.toString()); + // TODO better integrate it in the edition lifecycle + videodata.getSession().save(); + load(browser); + } catch (RepositoryException e1) { + throw new JcrException("Cannot update " + mediaobject, e1); + } + + } + }); + + Button deleteB = new Button(editor, SWT.FLAT); + deleteB.setText("Delete"); + deleteB.addSelectionListener(new Selected() { + + @Override + public void widgetSelected(SelectionEvent e) { + try { + mediaobject.remove(); + Composite parent = getParent(); + dispose(); + parent.layout(true, true); + } catch (RepositoryException e1) { + throw new JcrException("Cannot update " + mediaobject, e1); + } + + } + }); + } + + // TODO caption return browser; } public void load(Control control) { - Browser browser = (Browser) control; try { - getNode().getSession(); - String src = getNode().getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get()) - .getProperty(DbkAttr.fileref.name()).getString(); - // TODO manage self-hosted videos - // TODO for YouTube videos, check whether the URL starts with - // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v= - StringBuilder html = new StringBuilder(); - html.append( - "