download bz video

一、主方法

public void downloadBzVideo(String videoUrl, String recordId) {
        try {
            if (StringUtil.isEmpty(videoUrl)) {
                log.error("B站 no video url!");
            }
            //创建视频目录
            File videoDir = makeFileDir(BZ_VIDEO_FILE_DIR, recordId);
            //创建ffmpeg日志目录
            File logDir = makeFileDir(BZ_LOG_FILE_DIR, recordId);
            //声明音频文件、视频文件和最后合成视频文件,方便后面操作。也可以后面直接指定路径
            File audioFile = new File(videoDir.getAbsolutePath() + "/" + recordId + ".mp3");
            File videoFile = new File(videoDir.getAbsolutePath() + "/" + recordId + ".mp4");
            File finalVideoFile = new File(videoDir.getAbsolutePath() + "/" + recordId + "_final.mp4");
            downloadBzVideoToFile(videoUrl, audioFile, videoFile, finalVideoFile, logDir);
            //删除创建的目录,避免线上频繁创建文件使用内存过大。(视情况是否删除文件)
            deleteFileDir(videoDir, logDir);
        } catch (Exception e) {
            log.error("B站视频下载失败,URL为:={},详细信息:", videoUrl, e);
        }
    }

二、下载视频到本地

private void downloadBzVideoToFile(String url, File audioFile, File videoFile, File finalVideoFile, File logDir)
        throws Exception {
        String referer = "https://www.bilibili.com";
        String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36";
        String playInfo = "";
        Pattern playInfoPattern = Pattern.compile("<script>window.__playinfo__=(.*?)</script>");
        HttpHeaders headers = new HttpHeaders();
        headers.set("Referer", referer);
        headers.set("User-Agent", userAgent);
        headers.set(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN_VALUE);
        // Accept-Encoding 头,表示客户端接收gzip格式的压缩
        headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
        HttpEntity<?> requestEntity = new HttpEntity<>(headers);
        RestTemplate template = new RestTemplate();
        ResponseEntity<byte[]> response = template.exchange(url,
            HttpMethod.GET, requestEntity, byte[].class);
        if (!response.getStatusCode().is2xxSuccessful()) {
            // TODO 非200响应
            log.error("请求失败,url:{},status:{}", url, response.getStatusCode());
            return;
        }
        String contentEncoding = response.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
        if ("gzip".equals(contentEncoding)) { // gzip编码
            // gzip解压服务器的响应体
            byte[] data = unGZip(new ByteArrayInputStream(response.getBody()));
            String responseBody = new String(data, StandardCharsets.UTF_8);
			//解析音频、视频文件
            Matcher playInfoMatcher = playInfoPattern.matcher(responseBody);
            if (playInfoMatcher.find()) {
                playInfo = playInfoMatcher.group(1);
            }
            JSONObject jsonMap = JSONObject.parseObject(playInfo);
            String audioUrl = jsonMap.getJSONObject("data").getJSONObject("dash").getJSONArray("audio")
                .getJSONObject(0)
                .getString("base_url");
            String videoUrl = jsonMap.getJSONObject("data").getJSONObject("dash").getJSONArray("video")
                .getJSONObject(0)
                .getString("base_url");
			//将音频、视频内容写入文件
            byte[] audioContent = downloadBytes(audioUrl, referer, userAgent);
            byte[] videoContent = downloadBytes(videoUrl, referer, userAgent);
            saveContentToFile(audioFile, audioContent);
            saveContentToFile(videoFile, videoContent);
				
			//执行ffmpeg命令,将音视频文件合并	
            String cmd = String.format(
                ffmpegPath + " -i " + videoFile.getAbsolutePath() + " -y -i "
                    + audioFile.getAbsolutePath() + " -c:v copy -c:a copy -strict experimental "
                    + finalVideoFile.getAbsolutePath());
            execCommand(cmd, logDir);
        }
    }

三、ffmpeg相关调用方法


private void execCommand(String cmd, File logFileDir) {
        StringBuilder stdoutContent = new StringBuilder();
        StringBuilder stderrContent = new StringBuilder();
        execCommand(cmd, stdoutContent, stderrContent, logFileDir);
    }

    private void execCommand(String cmd, StringBuilder stdoutContent, StringBuilder stderrContent, File logFileDir) {
        java.lang.Process process = null;
        try {
            String osName = System.getProperty("os.name").toLowerCase();
            String[] cmds = new String[]{"cmd", "/c", cmd};
            if (osName.indexOf("linux") >= 0) {
                cmds = new String[]{"/bin/sh", "-c", cmd};
            }
            process = Runtime.getRuntime().exec(cmds, null, logFileDir);
            ByteArrayOutputStream stdoutOutStream = new ByteArrayOutputStream();
            ByteArrayOutputStream stderrOutStream = new ByteArrayOutputStream();
            InputStream errorInStream = new BufferedInputStream(process.getErrorStream());
            InputStream processInStream = new BufferedInputStream(process.getInputStream());
            int num = 0;
            byte[] bs = new byte[1024];
            while ((num = errorInStream.read(bs)) != -1) {
                stderrOutStream.write(bs, 0, num);
            }
            while ((num = processInStream.read(bs)) != -1) {
                stdoutOutStream.write(bs, 0, num);
            }
            process.waitFor();
            stdoutContent.append(new String(stdoutOutStream.toByteArray()));
            stderrContent.append(new String(stderrOutStream.toByteArray()));
            errorInStream.close();
            processInStream.close();
            stdoutOutStream.close();
            stderrOutStream.close();
        } catch (IOException e) {
//            log.error("failed to exec cmd: {}", cmd, e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (process != null) {
                    process.destroy();
                }
            } catch (Exception ex) {
                //ignore
            }
        }
    }

四、相关重构方法

    /**
     * 删除目录
     */
    private void deleteFileDir(File... fileDirs) {
        for (File fileDir : fileDirs) {
            File[] listFiles = fileDir.listFiles();
            if (listFiles != null) {
                for (File file : listFiles) {
                    file.delete();
                }
            }
            fileDir.delete();
        }
    }

    /**
     * 保存信息到指定文件
     */
    private void saveContentToFile(File file, byte[] content) throws Exception {
        BufferedOutputStream audioStream = new BufferedOutputStream(
            new FileOutputStream(file));
        audioStream.write(content);
        audioStream.close();
    }

    /**
     * 创建目录
     */
    public File makeFileDir(String newFileDir, String recordId) {
        File file = new File(newFileDir + "/" + recordId + "/");
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    /**
     * 下载url为字节码
     */
    private byte[] downloadBytes(String url, String referer, String userAgent) {
        HttpResponse response = HttpRequest.get(url)
            .header("Referer", referer)
            .header("User-Agent", userAgent)
            .setFollowRedirects(true).executeAsync();
        if (!response.isOk()) {
            throw new HttpException("Server response error with status code: [{}]",
                response.getStatus());
        } else {
            return response.bodyBytes();
        }
    }

    /**
     * Gzip解压缩
     */
    private byte[] unGZip(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
            byte[] buf = new byte[4096];
            int len = -1;
            while ((len = gzipInputStream.read(buf, 0, buf.length)) != -1) {
                byteArrayOutputStream.write(buf, 0, len);
            }
            return byteArrayOutputStream.toByteArray();
        } finally {
            byteArrayOutputStream.close();
        }
    }```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值