diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 570e64bac..ed625acf6 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -323,6 +323,9 @@ "cores": { "message": "Cores" }, + "copyVideoId": { + "message": "Copy Video Id" + }, "cropChapterTitles": { "message": "Crop chapter titles" }, diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index abc5e0c94..de14d4b1d 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -143,6 +143,9 @@ "cores": { "message": "核心" }, + "copyVideoId": { + "message": "复制影片 ID" + }, "cropChapterTitles": { "message": "裁剪章节标题" }, diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index 9e1c091f1..7b02f0014 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -143,6 +143,9 @@ "cores": { "message": "核心數" }, + "copyVideoId": { + "message": "複製影片 ID" + }, "cropChapterTitles": { "message": "截斷章節名稱" }, diff --git a/js&css/extension/www.youtube.com/styles.css b/js&css/extension/www.youtube.com/styles.css index 3711aedeb..336bad65b 100644 --- a/js&css/extension/www.youtube.com/styles.css +++ b/js&css/extension/www.youtube.com/styles.css @@ -266,6 +266,26 @@ body.no-scroll .it-player-button--tooltip { fill: #f00; } +/*------------------------------------------------------------------------------ +5.2 COPY VIDEO ID +------------------------------------------------------------------------------*/ +.it-playlist-copy-video-id { + width: 40px; + height: 40px; + cursor: pointer; + border: none; + outline: none; + background: none; + fill: var(--yt-spec-icon-inactive); + align-self: center; +} + +.it-playlist-copy-video-id:hover::after { + content: attr(data-tooltip); + transform: translateX(-50%); + color: var(--paper-tooltip-text-color, white); + background-color: var(--paper-tooltip-background, #616161); +} /*------------------------------------------------------------------------------ 6.0 CHANNEL diff --git a/js&css/web-accessible/core.js b/js&css/web-accessible/core.js index 8b167a2df..36eb49f8a 100644 --- a/js&css/web-accessible/core.js +++ b/js&css/web-accessible/core.js @@ -377,6 +377,15 @@ document.addEventListener('it-message-from-extension', function () { } break + case 'copyVideoId': + if (ImprovedTube.storage.copy_video_id === false) { + document.querySelector('.improvedtube-player-button[data-tooltip="CopyVideoId"]')?.remove(); + } else if (ImprovedTube.storage.copy_video_id === true) { + document.querySelectorAll('.improvedtube-player-button').forEach(e => e.remove()); + ImprovedTube.improvedtubeYoutubeButtonsUnderPlayer(); + } + break + case 'dayOfWeek': if (ImprovedTube.storage.day_of_week === false) { document.querySelector(".ytd-day-of-week")?.remove(); @@ -417,6 +426,13 @@ document.addEventListener('it-message-from-extension', function () { if (playlistData.currentIndex != playlistData.localCurrentIndex) { playlistData.currentIndex = playlistData.localCurrentIndex;} } break + case 'playlistCopyVideoId': + if (ImprovedTube.storage.playlist_copy_video_id === false) { + document.querySelectorAll('.it-playlist-copy-video-id').forEach(e => e.remove()); + } else if (ImprovedTube.storage.playlist_copy_video_id === true) { + ImprovedTube.playlistCopyVideoIdButton(); + } + break } // dont trigger shortcuts on config change, reinitialize handler instead diff --git a/js&css/web-accessible/init.js b/js&css/web-accessible/init.js index ca6596ca6..f94dd692e 100644 --- a/js&css/web-accessible/init.js +++ b/js&css/web-accessible/init.js @@ -82,6 +82,7 @@ ImprovedTube.init = function () { ImprovedTube.playlistShuffle(); ImprovedTube.playlistReverse(); ImprovedTube.playlistPopup(); + ImprovedTube.playlistCopyVideoIdButton(); } }); this.pageType(); diff --git a/js&css/web-accessible/www.youtube.com/appearance.js b/js&css/web-accessible/www.youtube.com/appearance.js index e92dae4bb..222eb871c 100644 --- a/js&css/web-accessible/www.youtube.com/appearance.js +++ b/js&css/web-accessible/www.youtube.com/appearance.js @@ -460,6 +460,32 @@ ImprovedTube.improvedtubeYoutubeButtonsUnderPlayer = function () { svg.appendChild(path); button.appendChild(svg); section.insertAdjacentElement('afterend', button) } + + if (this.storage.copy_video_id !== false) { + var button = document.createElement('button'), + svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), + path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + + button.className = 'improvedtube-player-button'; + button.dataset.tooltip = 'CopyVideoId'; + svg.style.opacity = '.5'; + svg.setAttributeNS(null, 'viewBox', '0 0 24 24'); + path.setAttributeNS(null, 'd', 'M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z'); + button.onclick = function () { + svg.style.opacity = '1'; + const videoURL = ImprovedTube.elements.player?.getVideoUrl(); + const videoId = videoURL.match(ImprovedTube.regex.video_id)?.[1]; + navigator.clipboard.writeText(videoId); + button.dataset.tooltip = 'Copied!'; + setTimeout(function() { + button.dataset.tooltip = 'CopyVideoID'; + svg.style.opacity = '.5'; + }, 500); + } + + svg.appendChild(path); button.appendChild(svg); + section.insertAdjacentElement('afterend', button) + } } } }; diff --git a/js&css/web-accessible/www.youtube.com/playlist.js b/js&css/web-accessible/www.youtube.com/playlist.js index 79628849f..d38fd9a65 100644 --- a/js&css/web-accessible/www.youtube.com/playlist.js +++ b/js&css/web-accessible/www.youtube.com/playlist.js @@ -214,6 +214,46 @@ ImprovedTube.playlistPopupCreateButton = function (playlistID, altButtonStyle, c return button; }; + +/*------------------------------------------------------------------------------ +4.5.6 PLAYLIST COPY VIDEO ID BUTTON +------------------------------------------------------------------------------*/ +/** +*/ +ImprovedTube.playlistCopyVideoIdButton = function () { + if (this.storage.playlist_copy_video_id === true) { + const playlistItems = document.querySelectorAll('ytd-playlist-panel-video-renderer'); + playlistItems.forEach(item => { + if (!item.querySelector('.it-playlist-copy-video-id')) { + const button = document.createElement('button'); + svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + button.className = ('it-playlist-copy-video-id'); + button.dataset.tooltip = 'CopyVideoId'; + svg.setAttributeNS(null, 'viewBox', '0 0 24 24'); + path.setAttributeNS(null, 'd', 'M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z'); + svg.append(path); + button.append(svg); + button.addEventListener('click', () => { + const playlistLink = item.querySelector('a#wc-endpoint'); + if (playlistLink) { + const playlistURL = playlistLink.href.match(ImprovedTube.regex.video_id)?.[1]; + navigator.clipboard.writeText(playlistURL); + } + button.dataset.tooltip = 'Copied!'; + setTimeout(function() { + button.dataset.tooltip = 'CopyVideoID'; + }, 500); + }); + const menuElement = item.querySelector('#menu'); + if (menuElement) { + menuElement.parentNode.insertBefore(button, menuElement); + } + } + }); + } +} + /** * ## Adds a playlist popup button to each playlist panel found or update the links of existing popup buttons * - buttons will be added on the playlist page (next to the share button), in the playlist panel (after the loop and shuffle buttons), and/or the mini playlist section of the mini player (after the loop and shuffle buttons) diff --git a/menu/skeleton-parts/player.js b/menu/skeleton-parts/player.js index 2834f96b1..0bb9b2d05 100644 --- a/menu/skeleton-parts/player.js +++ b/menu/skeleton-parts/player.js @@ -1170,6 +1170,11 @@ extension.skeleton.main.layers.section.player.on.click = { component: 'switch', text: 'loop', value: true + }, + copy_video_id: { + component: 'switch', + text: 'copyVideoId', + value: true } }, player_hide_controls_options: { diff --git a/menu/skeleton-parts/playlist.js b/menu/skeleton-parts/playlist.js index 6aba3bf24..512775548 100644 --- a/menu/skeleton-parts/playlist.js +++ b/menu/skeleton-parts/playlist.js @@ -42,6 +42,10 @@ extension.skeleton.main.layers.section.playlist = { playlist_popup: { component: 'switch', text: 'popupPlayer' + }, + playlist_copy_video_id: { + component: 'switch', + text: 'copyVideoId' } } }