Wiki source code of VideoUpload
Last modified by RPG Research Xwiki Documents Administrator on 2025/09/07 16:44
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
| |
1.1 | 1 | {{include reference="XWikiTubeCode.Macros"/}} |
| 2 | |||
| 3 | {{velocity}} | ||
| 4 | #* Upload and transcode files *# | ||
| 5 | #set($videoUploader = $xwiki.parseGroovyFromPage("XWikiTubeCode.VideoUploadGroovy")) | ||
| 6 | #set($discard = $videoUploader.init($xwiki, $xcontext, $services)) | ||
| 7 | #set($discard = $xcontext.put("videoUploader", $videoUploader)) | ||
| 8 | #set($action = $request.action) | ||
| 9 | |||
| 10 | #if ("$!action" != '') | ||
| 11 | #if ($action == "uploadVideo") | ||
| 12 | ## Check the video target document | ||
| 13 | #set($targetDocRef = "") | ||
| 14 | #if ("$!request.targetDocRef" != '' && $xwiki.exists($request.targetDocRef)) | ||
| 15 | #set($targetDocRef = $request.targetDocRef) | ||
| 16 | #end | ||
| 17 | #set($rep = $videoUploader.uploadVideo("videoInput", $targetDocRef)) | ||
| 18 | #if ($rep.ret == true) | ||
| 19 | ## Display video information | ||
| 20 | #displayVideoInfo($rep.video) | ||
| 21 | ## Encode the video | ||
| 22 | #set($ffmpeg = $videoUploader.mediaTranscoder.checkFFmpeg()) | ||
| 23 | #if ($ffmpeg) | ||
| 24 | #set($video = $rep.video) | ||
| 25 | #set($videoDocRef = $video.videoDocRef) | ||
| 26 | #if("$!videoDocRef" != '' && $xwiki.exists($videoDocRef)) | ||
| 27 | #set($discard = $videoUploader.encodeVideo($videoDocRef, "libvpx-vp9")) | ||
| 28 | #set($videoDoc = $xwiki.getDocument($videoDocRef)) | ||
| 29 | #set($videoObj = $videoDoc.getObject("XWikiTubeCode.MediaClass", "original", "1")) | ||
| 30 | #if("$!videoObj" != '') | ||
| 31 | #set($discard = $videoObj.set("encoded", 1)) | ||
| 32 | #set($discard = $videoDoc.save()) | ||
| 33 | #end | ||
| 34 | #end | ||
| 35 | #end | ||
| 36 | #else | ||
| 37 | #if($rep.error == "file_exists") | ||
| 38 | {{warning}}The video name alreay exists.{{/warning}} | ||
| 39 | #end | ||
| 40 | #end | ||
| 41 | #elseif($action == 'getXWikiTubeVideos') | ||
| 42 | ## List all XWiki videos filtred by user | ||
| 43 | #set($offset = $mathtool.toInteger($request.get('offset'))) | ||
| 44 | #set($offset = $offset - 1) | ||
| 45 | #if($offset < 0) | ||
| 46 | #set($offset = 0) | ||
| 47 | #end | ||
| 48 | #set($limit = $mathtool.toInteger($request.get('limit'))) | ||
| 49 | #if(!$hasAdmin) | ||
| 50 | #set($userFilter = " and doc.author=${xcontext.user}") | ||
| 51 | #end | ||
| 52 | #set($filterName = "$!{request.get('name')}") | ||
| 53 | #if("$!filterName" != '') | ||
| 54 | #set($filterNameCond = " and media.name like :filterName") | ||
| 55 | #end | ||
| 56 | #set($xwql = "from doc.object(XWikiTubeCode.MediaClass) as media where doc.fullName<>'XWikiTubeCode.MediaTemplate' and media.original=1 $!userFilter $!filterNameCond order by doc.creationDate") | ||
| 57 | #set($query = $services.query.xwql($xwql)) | ||
| 58 | #if("$!filterName" != '') | ||
| 59 | #set($query = $query.bindValue("filterName","%$filterName%")) | ||
| 60 | #end | ||
| 61 | #set($totalRows = $query.count()) | ||
| 62 | ##set($results = $query.setLimit($limit).setOffset($offset).execute()) | ||
| 63 | #set($results = $query.execute()) | ||
| 64 | #set($returnedRows = $results.size()) | ||
| 65 | ## === | ||
| 66 | ## JSON | ||
| 67 | ## === | ||
| 68 | #set($rows = []) | ||
| 69 | #foreach($mediaDocRef in $results) | ||
| 70 | #set($mediaDoc = $xwiki.getDocument($mediaDocRef)) | ||
| 71 | #set($mediaObj = $mediaDoc.getObject("XWikiTubeCode.MediaClass")) | ||
| 72 | #set($name = $mediaObj.getValue("name")) | ||
| 73 | #set($extension = $mediaObj.getValue("extension")) | ||
| 74 | #set($mimeType = $mediaObj.getValue("mimeType")) | ||
| 75 | #set($size = $mathtool.div($mediaObj.getValue("size"), 1000000)) | ||
| 76 | #set($duration = $mediaObj.getValue("duration")) | ||
| 77 | #set($resolution = $mediaObj.getValue("resolution")) | ||
| 78 | #set($creator = $xwiki.getUserName($mediaDoc.author, false)) | ||
| 79 | #set($creationDate = $xwiki.formatDate($mediaDoc.creationDate)) | ||
| 80 | #set($encoded = "False") | ||
| 81 | #if($mediaObj.getValue("encoded") == "1") | ||
| 82 | #set($encoded = "True") | ||
| 83 | #end | ||
| 84 | #set($mediaURL = $xwiki.getURL($mediaDocRef)) | ||
| 85 | #set($actionLinks = "<div class='actionsrow'>") | ||
| 86 | #set($actionLinks = "${actionLinks}<div class='actionsrow'><a href='$mediaURL' rel='${mediaDocRef}' class='glyphicon glyphicon-play' title='Play'></a>") | ||
| 87 | #if($hasAdmin || ($xcontext.user == $mediaDoc.author)) | ||
| 88 | #set($actionLinks = "${actionLinks}<a href='javascript:;' rel='${mediaDocRef}' class='glyphicon glyphicon-cog' title='Encode'></a>") | ||
| 89 | #set($actionLinks = "${actionLinks}<a href='javascript:;' rel='${mediaDocRef}' class='glyphicon glyphicon-trash delete-media' title='Delete'></a>") | ||
| 90 | #end | ||
| 91 | #set($actionLinks = "${actionLinks}</div>") | ||
| 92 | #set($row = {"doc_viewable": true, "name": $name, "extension": $extension, "mimeType": $mimeType, "size": "${size} MB", "duration": "${duration} s", "resolution": $resolution, "creator": "$creator", "creationDate": $creationDate, "encoded": $encoded, "actions": $actionLinks}) | ||
| 93 | #set($discard = $rows.add($row)) | ||
| 94 | #end | ||
| 95 | { | ||
| 96 | "totalrows": $totalRows, | ||
| 97 | "matchingtags": {}, | ||
| 98 | "tags" : [], | ||
| 99 | "returnedrows": $returnedRows, | ||
| 100 | "offset": ($offset + 1), | ||
| 101 | "reqNo": $mathtool.toInteger($request.reqNo), | ||
| 102 | "rows": $jsontool.serialize($rows) | ||
| 103 | } | ||
| 104 | #elseif($action == 'deleteMedia') | ||
| 105 | #if("$!request.mediaDocRef" != "" && $xwiki.exists($request.mediaDocRef)) | ||
| 106 | #set($mediaDocRef = $request.mediaDocRef) | ||
| 107 | #set($rep = $videoUploader.deleteMedia($mediaDocRef)) | ||
| 108 | $rep | ||
| 109 | #end | ||
| 110 | #elseif($action == 'selectVideo') | ||
| 111 | #if("$!request.videoDocRef" != "" && $xwiki.exists($request.videoDocRef)) | ||
| 112 | #set($videoDocRef = $request.videoDocRef) | ||
| 113 | #set($videoDoc = $xwiki.getDocument($videoDocRef)) | ||
| 114 | #set($attachments = $videoDoc.getAttachmentList()) | ||
| 115 | #set($videoAttachments = []) | ||
| 116 | #foreach($att in $attachments) | ||
| 117 | #if($att.getMimeType().contains("video")) | ||
| 118 | #set($discard = $videoAttachments.add($att)) | ||
| 119 | #end | ||
| 120 | #end | ||
| 121 | {{html}}#displayVideoEncodeForm($videoAttachments){{/html}} | ||
| 122 | #end | ||
| 123 | #elseif($action == 'initVideoEncoding') | ||
| 124 | #if("$!request.videoDocRef" != '' && $xwiki.exists($request.videoDocRef) && "$!request.videoName" != '') | ||
| 125 | #set($videoDocRef = $request.videoDocRef) | ||
| 126 | #set($videoName = $request.videoName) | ||
| 127 | #set($newLine = $escapetool.getNewline()) | ||
| 128 | #set($rep = $videoUploader.encodeAttachmentVideo($videoDocRef, $videoName)) | ||
| 129 | #if($rep) | ||
| 130 | $videoUploader.logger.info("${newLine}Video encoding initialization phase succeed, document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 131 | #else | ||
| 132 | $videoUploader.logger.severe("${newLine}Video encoding initialization phase has failed, document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 133 | #end | ||
| 134 | {"initStatus": $rep.ret, "error": "$!rep.error"} | ||
| 135 | #end | ||
| 136 | #elseif($action == 'videoEncode') | ||
| 137 | #if("$!request.videoDocRef" != '' && $xwiki.exists($request.videoDocRef) && "$!request.videoName" != '') | ||
| 138 | #set($videoDocRef = $request.videoDocRef) | ||
| 139 | #set($videoName = $request.videoName) | ||
| 140 | #set($videoDoc = $xwiki.getDocument($videoDocRef)) | ||
| 141 | #set($waitingCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "waiting")) | ||
| 142 | #set($completeCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "complete")) | ||
| 143 | #set($noAudioStreamCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "noaudiostream")) | ||
| 144 | #set($noVideoStreamCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "novideostream")) | ||
| 145 | #set($discard = $completeCommands.addAll($noAudioStreamCommands)) | ||
| 146 | #set($discard = $completeCommands.addAll($noVideoStreamCommands)) | ||
| 147 | #set($runingCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "transcoding")) | ||
| 148 | #set($transcodingErrorCommands = $videoDoc.getObjects("XWikiTubeCode.CommandClass", "status", "transcoding_error")) | ||
| 149 | #set($discard = $runingCommands.addAll($transcodingErrorCommands)) | ||
| 150 | #set($newLine = $escapetool.getNewline()) | ||
| 151 | |||
| 152 | ## Check waiting commands | ||
| 153 | #set($waitingCmdsJSON = []) | ||
| 154 | #if($waitingCommands.size() > 0) | ||
| 155 | ## Check if there is no runing command and check also if FFmpeg is not runing | ||
| 156 | #set($ffmpegRunning = $videoUploader.mediaTranscoder.checkFFmpegRunning()) | ||
| 157 | #if(!$ffmpegRunning && $runingCommands.size() == 0) | ||
| 158 | ## Start a new command | ||
| 159 | #set($nextCmd = $waitingCommands[0]) | ||
| 160 | #if($nextCmd.outputFile != "" && !$nextCmd.outputFile.contains("manifest")) | ||
| 161 | ## video and audio streams commands | ||
| 162 | #set($sourceVideoPath = $videoUploader.fileNameUtils.concat($nextCmd.getValue("workDir"), $videoName)) | ||
| 163 | #set($commandStatus = "transcoding") | ||
| 164 | #if($nextCmd.outputFile.contains("audio")) ## audio | ||
| 165 | ## Check if the original video has audio stream | ||
| 166 | #if($videoUploader.mediaTranscoder.hasAudioStream($sourceVideoPath)) | ||
| 167 | ## Start the command | ||
| 168 | #set($rep = $videoUploader.commandRunner.run(["bash", "-c", $nextCmd.getValue("cmd")], '', 2147483647)) | ||
| 169 | $videoUploader.logger.info("${newLine}Start FFmpeg command «${nextCmd.getValue('cmd')}» , document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 170 | #else | ||
| 171 | ## Update the command status to complete | ||
| 172 | #set($commandStatus = "noaudiostream") | ||
| 173 | #end | ||
| 174 | #else | ||
| 175 | ## Check if the original video has video stream | ||
| 176 | #if($videoUploader.mediaTranscoder.hasVideoStream($sourceVideoPath)) | ||
| 177 | ## Start the command | ||
| 178 | #set($rep = $videoUploader.commandRunner.run(["bash", "-c", $nextCmd.getValue("cmd")], '', 2147483647)) | ||
| 179 | $videoUploader.logger.info("${newLine}Start FFmpeg command «${nextCmd.getValue('cmd')}» , document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 180 | #else | ||
| 181 | ## Update the command status to complete | ||
| 182 | #set($commandStatus = "novideostream") | ||
| 183 | #end | ||
| 184 | #end | ||
| 185 | ## Update the status of the command object | ||
| 186 | #set($cmdObj = $videoDoc.getObject("XWikiTubeCode.CommandClass", "outputFile", $nextCmd.outputFile)) | ||
| 187 | #if($cmdObj) | ||
| 188 | #set($discard = $cmdObj.set("status", $commandStatus)) | ||
| 189 | #set($discard = $videoDoc.save()) | ||
| 190 | #end | ||
| 191 | #elseif($nextCmd.outputFile.contains("manifest")) | ||
| 192 | ## Manifest file command | ||
| 193 | ## Generate the manifest command and update the command object | ||
| 194 | #set($manifestCmd = $videoUploader.mediaTranscoder.generateDASHManifestCommand($nextCmd.getValue("workDir"))) | ||
| 195 | #set($rep = $videoUploader.commandRunner.run(["bash", "-c", $manifestCmd], '', 2147483647)) | ||
| 196 | $videoUploader.logger.info("${newLine}Start FFmpeg command «${manifestCmd}» , document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 197 | ## Update the status/cmd properties of the command object | ||
| 198 | #set($cmdObj = $videoDoc.getObject("XWikiTubeCode.CommandClass", "outputFile", $nextCmd.outputFile)) | ||
| 199 | #if($cmdObj) | ||
| 200 | #set($discard = $cmdObj.set("status", "transcoding")) | ||
| 201 | #set($discard = $cmdObj.set("cmd", $manifestCmd)) | ||
| 202 | #set($discard = $videoDoc.save()) | ||
| 203 | #end | ||
| 204 | #end | ||
| 205 | #end | ||
| 206 | #foreach($cmd in $waitingCommands) | ||
| 207 | #set($discard = $waitingCmdsJSON.add({"progress": 0, "stream": $cmd.outputFile.replace("_output.txt", "")})) | ||
| 208 | #end | ||
| 209 | #end | ||
| 210 | |||
| 211 | ## Check runing commands | ||
| 212 | #set($encodeError = false) | ||
| 213 | #set($runingCmdsJSON = []) | ||
| 214 | #foreach($cmd in $runingCommands) | ||
| 215 | #set($outputFilePath = "${cmd.workDir}/${cmd.outputFile}") | ||
| 216 | #set($errorFilePath = "${cmd.workDir}/${cmd.outputFile.replace('output', 'error')}") | ||
| 217 | #set($progressRep = $videoUploader.checkEncodingProgress($outputFilePath, $errorFilePath, $cmd.getValue("duration"))) | ||
| 218 | #if($progressRep.status == "end") ## Command complete | ||
| 219 | ## Update the command object | ||
| 220 | #set($cmdObj = $videoDoc.getObject("XWikiTubeCode.CommandClass", "outputFile", $cmd.outputFile)) | ||
| 221 | #if($cmdObj) | ||
| 222 | #set($discard = $cmdObj.set("status", "complete")) | ||
| 223 | #set($discard = $videoDoc.save()) | ||
| 224 | $videoUploader.logger.info("${newLine}FFmpeg command successfully completed «${cmd.getValue('cmd')}» , document=${videoDocRef}, video=${videoName}${newLine}") | ||
| 225 | #end | ||
| 226 | #set($discard = $runingCmdsJSON.add({"progress": 100, "stream": $cmd.outputFile.replace("_output.txt", "")})) | ||
| 227 | #elseif($progressRep.status == "empty_file") ## FFmpeg Error | ||
| 228 | #set($discard = $runingCmdsJSON.add({"progress": 0, "stream": $cmd.outputFile.replace("_output.txt", ""), "error": $!progressRep.error})) | ||
| 229 | #set($encodeError = true) | ||
| 230 | ## Update the command object | ||
| 231 | #set($cmdObj = $videoDoc.getObject("XWikiTubeCode.CommandClass", "outputFile", $cmd.outputFile)) | ||
| 232 | #if($cmdObj) | ||
| 233 | #set($discard = $cmdObj.set("status", "transcoding_error")) | ||
| 234 | #set($discard = $videoDoc.save()) | ||
| 235 | $videoUploader.logger.severe("${newLine}FFmpeg command error «${cmd.getValue('cmd')}» , document=${videoDocRef}, video=${videoName}, error=$!{progressRep.error}${newLine}") | ||
| 236 | #end | ||
| 237 | #else | ||
| 238 | #set($discard = $runingCmdsJSON.add({"progress": $progressRep.progress, "stream": $cmd.outputFile.replace("_output.txt", "")})) | ||
| 239 | #end | ||
| 240 | #end | ||
| 241 | ## | ||
| 242 | |||
| 243 | ## Check complete commands | ||
| 244 | #set($completeCmdsJSON = []) | ||
| 245 | #foreach($cmd in $completeCommands) | ||
| 246 | #set($discard = $completeCmdsJSON.add({"progress": 100, "stream": $cmd.outputFile.replace("_output.txt", "")})) | ||
| 247 | #end | ||
| 248 | ## | ||
| 249 | $jsontool.serialize({"encodeError": $encodeError, "waitingCmds": $waitingCmdsJSON, "completeCmds": $completeCmdsJSON, "runingCmds": $runingCmdsJSON}) | ||
| 250 | #end | ||
| 251 | #elseif($action == 'attachEncodedFiles') | ||
| 252 | #if("$!request.videoDocRef" != '' && $xwiki.exists($request.videoDocRef) && "$!request.videoName" != '') | ||
| 253 | #set($videoDocRef = $request.videoDocRef) | ||
| 254 | #set($videoName = $request.videoName) | ||
| 255 | #set($videoDoc = $xwiki.getDocument($videoDocRef)) | ||
| 256 | #set($originalVideoObj = $videoDoc.getObject("XWikiTubeCode.MediaClass", "original", 1)) | ||
| 257 | #set($newLine = $escapetool.getNewline()) | ||
| 258 | #if($originalVideoObj && $originalVideoObj.getValue("name") == $videoName && $originalVideoObj.getValue("encoded") != 1) | ||
| 259 | ## Start files attachments | ||
| 260 | #set($videoDir = $originalVideoObj.getValue("dirPath")) | ||
| 261 | #set($rep = $videoUploader.attachEncodingFilesToDoc($videoDir, $videoDocRef, $videoName)) | ||
| 262 | #if($rep) | ||
| 263 | $videoUploader.logger.info("${newLine}Attaching encoded files to document success: document=$videoDocRef, video=${videoName}${newLine}") | ||
| 264 | #else | ||
| 265 | $videoUploader.logger.severe("${newLine}Attaching encoded files to document fails: document=$videoDocRef, video=${videoName}${newLine}") | ||
| 266 | #end | ||
| 267 | $rep | ||
| 268 | #end | ||
| 269 | #end | ||
| 270 | #end | ||
| 271 | #end | ||
| 272 | {{/velocity}} |