diff --git a/src/assets/css/librarybrowser.scss b/src/assets/css/librarybrowser.scss index 691e50bf84..dd747c42bd 100644 --- a/src/assets/css/librarybrowser.scss +++ b/src/assets/css/librarybrowser.scss @@ -992,6 +992,10 @@ div.itemDetailGalleryLink.defaultCardBackground { border-collapse: collapse; } +.mediaInfoContent .btnCopy .material-icons { + font-size: inherit; +} + .mediaInfoStream { margin: 0 3em 0 0; display: inline-block; @@ -1000,6 +1004,10 @@ div.itemDetailGalleryLink.defaultCardBackground { .mediaInfoStreamType { display: block; + margin: 0.622em 0; /* copy button height compensation */ +} + +.layout-tv .mediaInfoStreamType { margin: 1em 0; } diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index b42584d016..56fd3705db 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -1,4 +1,5 @@ import browser from '../scripts/browser'; +import { copy } from '../scripts/clipboard'; import globalize from '../scripts/globalize'; import actionsheet from './actionSheet/actionSheet'; import { appHost } from './apphost'; @@ -366,32 +367,11 @@ import toast from './toast/toast'; break; case 'copy-stream': { const downloadHref = apiClient.getItemDownloadUrl(itemId); - const textAreaCopy = function () { - const textArea = document.createElement('textarea'); - textArea.value = downloadHref; - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - - if (document.execCommand('copy')) { - toast(globalize.translate('CopyStreamURLSuccess')); - } else { - prompt(globalize.translate('CopyStreamURL'), downloadHref); - } - document.body.removeChild(textArea); - }; - - /* eslint-disable-next-line compat/compat */ - if (navigator.clipboard === undefined) { - textAreaCopy(); - } else { - /* eslint-disable-next-line compat/compat */ - navigator.clipboard.writeText(downloadHref).then(function () { - toast(globalize.translate('CopyStreamURLSuccess')); - }).catch(function () { - textAreaCopy(); - }); - } + copy(downloadHref).then(() => { + toast(globalize.translate('CopyStreamURLSuccess')); + }).catch(() => { + prompt(globalize.translate('CopyStreamURL'), downloadHref); + }); getResolveFunction(resolve, id)(); break; } diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index 37d19dc805..a61dd165de 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -7,6 +7,9 @@ import dialogHelper from '../dialogHelper/dialogHelper'; import layoutManager from '../layoutManager'; +import toast from '../toast/toast'; +import { copy } from '../../scripts/clipboard'; +import dom from '../../scripts/dom'; import globalize from '../../scripts/globalize'; import loading from '../loading/loading'; import '../../elements/emby-select/emby-select'; @@ -19,6 +22,12 @@ import '../../assets/css/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import template from './itemMediaInfo.template.html'; +// Do not add extra spaces between tags - they will be copied into the result +const copyButtonHtml = layoutManager.tv ? '' : + ``; +const attributeDelimiterHtml = layoutManager.tv ? '' : ': '; + function setMediaInfo(user, page, item) { let html = item.MediaSources.map(version => { return getMediaSourceHtml(user, item, version); @@ -28,12 +37,25 @@ import template from './itemMediaInfo.template.html'; } const mediaInfoContent = page.querySelector('#mediaInfoContent'); mediaInfoContent.innerHTML = html; + + for (const btn of mediaInfoContent.querySelectorAll('.btnCopy')) { + btn.addEventListener('click', () => { + const infoBlock = dom.parentWithClass(btn, 'mediaInfoStream') || dom.parentWithClass(btn, 'mediaInfoSource') || mediaInfoContent; + + copy(infoBlock.textContent).then(() => { + toast(globalize.translate('Copied')); + }).catch(() => { + console.error('Could not copy text'); + toast(globalize.translate('CopyFailed')); + }); + }); + } } function getMediaSourceHtml(user, item, version) { - let html = ''; + let html = '