Wiki source code of Uncategorized Videos
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | {{velocity}} | ||
2 | ## Collect video attachments on the current page (safe getters only) | ||
3 | #set($videoExtensions = ['mp4','webm','ogg','avi','mov','wmv','flv','m4v']) | ||
4 | #set($videos = []) | ||
5 | #foreach($att in $doc.getAttachmentList()) | ||
6 | #set($name = $att.getFilename()) | ||
7 | #set($lname = $name.toLowerCase()) | ||
8 | #foreach($ext in $videoExtensions) | ||
9 | #if($lname.endsWith("." + $ext)) | ||
10 | #set($discard = $videos.add($att)) | ||
11 | #break | ||
12 | #end | ||
13 | #end | ||
14 | #end | ||
15 | |||
16 | #if($videos.size() > 0) | ||
17 | {{html wiki="false" clean="false"}} | ||
18 | <div id="xwiki-video-manager" style="margin:20px 0;"> | ||
19 | <h2>📹 Video Manager for: ${escapetool.xml($doc.fullName)}</h2> | ||
20 | |||
21 | <!-- Management panel only in edit action --> | ||
22 | #if($xcontext.action == 'edit') | ||
23 | <div class="video-management-panel" style="background:#f8f9fa;border:1px solid #dee2e6;border-radius:8px;padding:15px;margin-bottom:20px;"> | ||
24 | <h3>🛠️ Video Management</h3> | ||
25 | <div style="margin-bottom:10px;"> | ||
26 | <button onclick="toggleVideoManager()" class="btn btn-primary">Manage Videos</button> | ||
27 | <button onclick="exportVideoList()" class="btn btn-secondary">Export Video List</button> | ||
28 | </div> | ||
29 | <div id="video-manager-controls" style="display:none;"> | ||
30 | <h4>Move Videos to Another Page:</h4> | ||
31 | <input type="text" id="target-page" placeholder="Space.PageName" style="width:300px;margin-right:10px;"> | ||
32 | <button onclick="moveSelectedVideos()" class="btn btn-warning">Move Selected</button> | ||
33 | <div style="margin-top:10px;font-size:12px;color:#666;"> | ||
34 | Select videos below and enter target page (e.g., "Main.MyPage") | ||
35 | </div> | ||
36 | </div> | ||
37 | </div> | ||
38 | #end | ||
39 | |||
40 | <!-- Display grid --> | ||
41 | <div class="video-display-grid" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:20px;"> | ||
42 | #set($i = 0) | ||
43 | #foreach($att in $videos) | ||
44 | #set($i = $i + 1) | ||
45 | #set($filename = $att.getFilename()) | ||
46 | #set($lname = $filename.toLowerCase()) | ||
47 | #set($url = $doc.getAttachmentURL($filename)) | ||
48 | #set($vid = "video_${i}") | ||
49 | |||
50 | ## MIME type from extension (basic) | ||
51 | #set($videoType = "video/mp4") | ||
52 | #if($lname.endsWith(".webm")) | ||
53 | #set($videoType = "video/webm") | ||
54 | #elseif($lname.endsWith(".ogg")) | ||
55 | #set($videoType = "video/ogg") | ||
56 | #elseif($lname.endsWith(".avi")) | ||
57 | #set($videoType = "video/x-msvideo") | ||
58 | #elseif($lname.endsWith(".mov")) | ||
59 | #set($videoType = "video/quicktime") | ||
60 | #elseif($lname.endsWith(".wmv")) | ||
61 | #set($videoType = "video/x-ms-wmv") | ||
62 | #elseif($lname.endsWith(".flv")) | ||
63 | #set($videoType = "video/x-flv") | ||
64 | #elseif($lname.endsWith(".m4v")) | ||
65 | #set($videoType = "video/mp4") | ||
66 | #end | ||
67 | |||
68 | <div class="video-container" style="border:1px solid #ddd;border-radius:8px;padding:15px;background:#fff;"> | ||
69 | <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;"> | ||
70 | <h4 style="margin:0;flex-grow:1;">${escapetool.xml($filename)}</h4> | ||
71 | #if($xcontext.action == 'edit') | ||
72 | <input type="checkbox" class="video-selector" data-video="${escapetool.xml($filename)}" style="margin-left:10px;"> | ||
73 | #end | ||
74 | </div> | ||
75 | |||
76 | <video id="${vid}" controls preload="metadata" style="width:100%;max-width:100%;border-radius:4px;"> | ||
77 | <source src="${url}" type="${videoType}"> | ||
78 | Your browser does not support the video tag. | ||
79 | </video> | ||
80 | |||
81 | <div class="video-controls" style="margin-top:10px;display:flex;flex-wrap:wrap;gap:5px;"> | ||
82 | <button onclick="playPause('${vid}')" class="btn btn-sm btn-primary">⏯️ Play/Pause</button> | ||
83 | <button onclick="skipTime('${vid}', -10)" class="btn btn-sm btn-secondary">⏪ -10s</button> | ||
84 | <button onclick="skipTime('${vid}', 10)" class="btn btn-sm btn-secondary">⏩ +10s</button> | ||
85 | <button onclick="changeSpeed('${vid}', 0.5)" class="btn btn-sm btn-info">0.5x</button> | ||
86 | <button onclick="changeSpeed('${vid}', 1)" class="btn btn-sm btn-info">1x</button> | ||
87 | <button onclick="changeSpeed('${vid}', 1.5)" class="btn btn-sm btn-info">1.5x</button> | ||
88 | <button onclick="changeSpeed('${vid}', 2)" class="btn btn-sm btn-info">2x</button> | ||
89 | <a href="${url}" download="${escapetool.xml($filename)}" class="btn btn-sm btn-success">📥 Download</a> | ||
90 | </div> | ||
91 | |||
92 | <div class="video-info" style="margin-top:10px;font-size:12px;color:#666;"> | ||
93 | <div>Size: $att.getLongSize() bytes</div> | ||
94 | <div>Modified: $xwiki.formatDate($att.getDate())</div> | ||
95 | <div id="${vid}_duration">Duration: Loading...</div> | ||
96 | </div> | ||
97 | </div> | ||
98 | #end | ||
99 | </div> | ||
100 | </div> | ||
101 | |||
102 | <script> | ||
103 | function playPause(id){const v=document.getElementById(id);if(v){v.paused?v.play():v.pause();}} | ||
104 | function skipTime(id,s){const v=document.getElementById(id);if(v){v.currentTime+=s;}} | ||
105 | function changeSpeed(id,sp){const v=document.getElementById(id);if(v){v.playbackRate=sp;}} | ||
106 | document.addEventListener('DOMContentLoaded',function(){ | ||
107 | document.querySelectorAll('video').forEach(function(v){ | ||
108 | v.addEventListener('loadedmetadata',function(){ | ||
109 | var d=Math.round(v.duration)||0,m=Math.floor(d/60),s=d%60; | ||
110 | var e=document.getElementById(v.id+'_duration'); | ||
111 | if(e){e.textContent='Duration: '+m+':' + String(s).padStart(2,'0');} | ||
112 | }); | ||
113 | }); | ||
114 | }); | ||
115 | function toggleVideoManager(){ | ||
116 | var c=document.getElementById('video-manager-controls'); | ||
117 | if(c){c.style.display=(c.style.display==='none'||!c.style.display)?'block':'none';} | ||
118 | } | ||
119 | function moveSelectedVideos(){ | ||
120 | const sel=[...document.querySelectorAll('.video-selector:checked')].map(x=>x.getAttribute('data-video')); | ||
121 | const target=(document.getElementById('target-page')||{}).value||''; | ||
122 | if(!target){alert('Please enter a target page (e.g., "Main.MyPage")');return;} | ||
123 | if(!sel.length){alert('Please select at least one video to move.');return;} | ||
124 | if(confirm('Move '+sel.length+' video(s) to '+target+'?\n\nVideos: '+sel.join(', '))){ | ||
125 | alert('Server-side move not implemented.\nSelected: '+sel.join(', ')+'\nTarget: '+target); | ||
126 | } | ||
127 | } | ||
128 | function exportVideoList(){ | ||
129 | const titles=[...document.querySelectorAll('.video-container h4')].map(h=>h.textContent); | ||
130 | const txt='Video List from '+window.location.href+'\n\n'+titles.join('\n'); | ||
131 | const blob=new Blob([txt],{type:'text/plain'});const url=URL.createObjectURL(blob); | ||
132 | const a=document.createElement('a');a.href=url;a.download='video-list.txt'; | ||
133 | document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url); | ||
134 | } | ||
135 | document.addEventListener('keydown',function(e){ | ||
136 | const v=document.querySelector('video:hover, video:focus'); if(!v) return; | ||
137 | switch(e.code){ | ||
138 | case 'Space': e.preventDefault(); v.paused?v.play():v.pause(); break; | ||
139 | case 'ArrowLeft': e.preventDefault(); v.currentTime-=10; break; | ||
140 | case 'ArrowRight': e.preventDefault(); v.currentTime+=10; break; | ||
141 | } | ||
142 | }); | ||
143 | </script> | ||
144 | |||
145 | <style> | ||
146 | .video-container:hover{box-shadow:0 4px 8px rgba(0,0,0,0.1);transition:box-shadow .3s ease;} | ||
147 | .btn{padding:4px 8px;border:1px solid #ddd;background:#f8f9fa;border-radius:4px;cursor:pointer;text-decoration:none;display:inline-block;} | ||
148 | .btn:hover{background:#e9ecef;} | ||
149 | .btn-primary{background:#007bff;color:#fff;border-color:#007bff;} | ||
150 | .btn-secondary{background:#6c757d;color:#fff;border-color:#6c757d;} | ||
151 | .btn-success{background:#28a745;color:#fff;border-color:#28a745;} | ||
152 | .btn-warning{background:#ffc107;color:#212529;border-color:#ffc107;} | ||
153 | .btn-info{background:#17a2b8;color:#fff;border-color:#17a2b8;} | ||
154 | .btn-sm{font-size:12px;padding:2px 6px;} | ||
155 | @media (max-width:768px){.video-display-grid{grid-template-columns:1fr}.video-controls{justify-content:center}} | ||
156 | </style> | ||
157 | {{/html}} | ||
158 | #else | ||
159 | {{html wiki="false" clean="false"}} | ||
160 | <div style="text-align:center;padding:40px;background:#f8f9fa;border-radius:8px;"> | ||
161 | <h3>📹 No Videos Found</h3> | ||
162 | <p>No video files are attached to this page.</p> | ||
163 | #if($xcontext.action == 'edit') | ||
164 | <p>You can upload video files using the attachment feature in XWiki.</p> | ||
165 | #end | ||
166 | </div> | ||
167 | {{/html}} | ||
168 | #end | ||
169 | {{/velocity}} |