@@ -35,12 +35,15 @@ const PlayerPage: React.FC<PageProps> = ({ playbackId, videoExists, shareUrl, po
3535 const [ showMetadata , setShowMetadata ] = useState ( false ) ;
3636 const [ metadataLoading , setMetadataLoading ] = useState ( false ) ;
3737 const [ metadata , setMetadata ] = useState < any > ( null ) ;
38+ const [ isMetadataCopied , setIsMetadataCopied ] = useState ( false ) ;
3839 const copyTimeoutRef = useRef < number | null > ( null ) ;
40+ const metadataCopyTimeoutRef = useRef < number | null > ( null ) ;
3941 const router = useRouter ( ) ;
4042
4143 useEffect ( ( ) => {
4244 return ( ) => {
4345 if ( copyTimeoutRef . current ) clearTimeout ( copyTimeoutRef . current ) ;
46+ if ( metadataCopyTimeoutRef . current ) clearTimeout ( metadataCopyTimeoutRef . current ) ;
4447 } ;
4548 } , [ ] ) ;
4649
@@ -201,6 +204,17 @@ const PlayerPage: React.FC<PageProps> = ({ playbackId, videoExists, shareUrl, po
201204 }
202205 } ;
203206
207+ const copyMetadata = ( ) => {
208+ if ( metadata ) {
209+ copy ( JSON . stringify ( metadata , null , 2 ) ) ;
210+ setIsMetadataCopied ( true ) ;
211+ metadataCopyTimeoutRef . current = window . setTimeout ( ( ) => {
212+ setIsMetadataCopied ( false ) ;
213+ metadataCopyTimeoutRef . current = null ;
214+ } , 2000 ) ;
215+ }
216+ } ;
217+
204218 if ( errorMessage ) {
205219 return (
206220 < Layout darkMode >
@@ -326,6 +340,35 @@ const PlayerPage: React.FC<PageProps> = ({ playbackId, videoExists, shareUrl, po
326340 >
327341 { showMetadata ? 'Hide Metadata' : 'Show Metadata' }
328342 </ a >
343+ { showMetadata && metadata && (
344+ < a
345+ onClick = { copyMetadata }
346+ onKeyPress = { copyMetadata }
347+ role = "button"
348+ tabIndex = { 0 }
349+ className = "copy-metadata-link"
350+ title = "Copy"
351+ >
352+ < svg
353+ width = "16"
354+ height = "16"
355+ viewBox = "0 0 16 16"
356+ fill = "none"
357+ xmlns = "http://www.w3.org/2000/svg"
358+ className = "copy-icon"
359+ >
360+ < path
361+ d = "M10.5 1.5H3.5C2.67157 1.5 2 2.17157 2 3V11H3.5V3H10.5V1.5Z"
362+ fill = "currentColor"
363+ />
364+ < path
365+ d = "M12.5 4.5H6.5C5.67157 4.5 5 5.17157 5 6V13C5 13.8284 5.67157 14.5 6.5 14.5H12.5C13.3284 14.5 14 13.8284 14 13V6C14 5.17157 13.3284 4.5 12.5 4.5ZM12.5 13H6.5V6H12.5V13Z"
366+ fill = "currentColor"
367+ />
368+ </ svg >
369+ { isMetadataCopied && < span className = "copy-success" > Copied!</ span > }
370+ </ a >
371+ ) }
329372 </ div >
330373 { showMetadata && (
331374 < div className = "metadata-panel" >
@@ -397,6 +440,9 @@ const PlayerPage: React.FC<PageProps> = ({ playbackId, videoExists, shareUrl, po
397440 margin-top: 15px;
398441 padding-top: 15px;
399442 border-top: 1px solid rgba(255, 255, 255, 0.1);
443+ display: flex;
444+ justify-content: space-between;
445+ align-items: center;
400446 }
401447 .metadata-link {
402448 color: #ccc;
@@ -412,6 +458,29 @@ const PlayerPage: React.FC<PageProps> = ({ playbackId, videoExists, shareUrl, po
412458 padding-top: 15px;
413459 border-top: 1px solid rgba(255, 255, 255, 0.1);
414460 }
461+ .copy-metadata-link {
462+ display: inline-flex;
463+ align-items: center;
464+ gap: 8px;
465+ color: #ccc;
466+ cursor: pointer;
467+ padding: 4px 8px;
468+ border-radius: 4px;
469+ transition: all 0.2s ease;
470+ }
471+ .copy-metadata-link:hover {
472+ color: #fff;
473+ background: rgba(255, 255, 255, 0.1);
474+ }
475+ .copy-icon {
476+ width: 16px;
477+ height: 16px;
478+ flex-shrink: 0;
479+ }
480+ .copy-success {
481+ font-size: 12px;
482+ color: #4ade80;
483+ }
415484 .metadata-loading {
416485 color: #ccc;
417486 font-style: italic;
0 commit comments