mirror of
https://github.com/Geniusay/ChopperBot.git
synced 2026-06-20 21:46:03 +08:00
B站视频发布
This commit is contained in:
@@ -3,6 +3,7 @@ package org.example.utils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
@@ -50,6 +51,13 @@ public class HttpClientUtil {
|
||||
return executeRequest(httpPost);
|
||||
}
|
||||
|
||||
public static String post(String url, UrlEncodedFormEntity urlEncodedFormEntity, Map<String, String> headers) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setEntity(urlEncodedFormEntity);
|
||||
addHeaders(httpPost, headers);
|
||||
return executeRequest(httpPost);
|
||||
}
|
||||
|
||||
public static String put(String url) {
|
||||
return executeRequest(new HttpPut(url));
|
||||
}
|
||||
|
||||
@@ -29,5 +29,19 @@
|
||||
<artifactId>edgegrid-signer-core</artifactId>
|
||||
<version>5.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>chopperbot-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.example.api;
|
||||
|
||||
import org.example.core.publisher.impl.BilibiliVideoPublisher;
|
||||
|
||||
/**
|
||||
* @author dhx
|
||||
* @date 2023/9/18 20:42
|
||||
*/
|
||||
public class BilibiliPublishApi {
|
||||
public static void PublishVideo(String videoPath,String devicePath,String Cookie,String coverPath){
|
||||
BilibiliVideoPublisher bilibiliVideoPublisher = new BilibiliVideoPublisher();
|
||||
bilibiliVideoPublisher.publishVideo(videoPath,devicePath,Cookie,coverPath);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
PublishVideo(
|
||||
"C:\\Users\\admin\\Downloads\\test.mp4", //视频路径
|
||||
"C:\\Users\\admin\\Desktop\\video_bin\\", //分割后视频存放路径
|
||||
"", //b站Cookie
|
||||
"C:\\Users\\admin\\Desktop\\OIP-C.jpg"); //封面路径
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.example.core.publisher;
|
||||
|
||||
/**
|
||||
* @author dhx
|
||||
* @date 2023/9/18 20:49
|
||||
*/
|
||||
public interface PlatformVideoPublisher {
|
||||
/**
|
||||
*
|
||||
* @param videoPath 视频本地路径
|
||||
* @param devicePath 视频分片后存放路径
|
||||
* @param Cookie B站用户Cookie
|
||||
* @param coverPath 视频封面本地路径
|
||||
*/
|
||||
void publishVideo(String videoPath,String devicePath,String Cookie,String coverPath);
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package org.example.core.publisher.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.example.core.publisher.PlatformVideoPublisher;
|
||||
import org.example.exception.ChopperBotException;
|
||||
import org.example.utils.FileUtil;
|
||||
import org.example.utils.HttpClientUtil;
|
||||
import org.example.utils.VideoDeviceUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
import java.util.*;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* @author dhx
|
||||
* @date 2023/9/18 20:49
|
||||
*/
|
||||
public class BilibiliVideoPublisher implements PlatformVideoPublisher {
|
||||
@Override
|
||||
public void publishVideo(String videoPath, String devicePath, String Cookie, String coverPath) {
|
||||
String jctPart = Cookie.substring(Cookie.indexOf("bili_jct"));
|
||||
String csrf;
|
||||
try {
|
||||
csrf = jctPart.substring(jctPart.indexOf("=") + 1, jctPart.indexOf(";"));
|
||||
System.out.println(csrf);
|
||||
} catch (Exception e) {
|
||||
throw new ChopperBotException("Cookie 错误");
|
||||
}
|
||||
int filesize = FileUtil.getFilesize(videoPath);
|
||||
if (filesize == -1) {
|
||||
throw new ChopperBotException("获取文件大小失败");
|
||||
}
|
||||
|
||||
//申请上传
|
||||
Map<String, String> header = new HashMap<>();
|
||||
header.put("Cookie", Cookie);
|
||||
header.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69");
|
||||
header.put("Referer", "https://member.bilibili.com/platform/upload/video/frame?spm_id_from=333.1007.top_bar.upload");
|
||||
String res = HttpClientUtil.get("https://member.bilibili.com/preupload?probe_version=20221109&upcdn=bda2&zone=cs&name=test.mp4&r=upos&profile=ugcfx%2Fbup&ssl=0&version=2.14.0.0&build=2140000&size=" + filesize + "&webVersion=2.14.0"
|
||||
, header);
|
||||
JSONObject mp4Obj = JSONObject.parseObject(res);
|
||||
String mp4_upos_uri = mp4Obj.getString("upos_uri");
|
||||
String put_query = mp4Obj.getString("put_query");
|
||||
String endpoint = mp4Obj.getString("endpoint");
|
||||
Long chunk_size = mp4Obj.getLong("chunk_size");
|
||||
Long biz_id = mp4Obj.getLong("biz_id");
|
||||
String auth = mp4Obj.getString("auth");
|
||||
|
||||
|
||||
String res2 = HttpClientUtil.get("https://member.bilibili.com/preupload?name=file_meta.txt&size=2000&r=upos&profile=fxmeta%2Fbup&ssl=0&version=2.14.0.0&build=2140000&webVersion=2.14.0", header);
|
||||
JSONObject txtObj = JSONObject.parseObject(res2);
|
||||
String meta_upos_uri = txtObj.getString("upos_uri");
|
||||
String preUploadUrl = String.format("https:%s%s?uploads&output=json%s&filesize=%s&partsize=%s&meta_upos_uri=%s&biz_id=%s", endpoint, mp4_upos_uri.substring(mp4_upos_uri.indexOf('/') + 1), put_query.substring(put_query.indexOf('&')), String.valueOf(filesize), chunk_size, meta_upos_uri, biz_id);
|
||||
Map<String, String> header2 = new HashMap<>();
|
||||
header2.put("X-Upos-Auth", auth);
|
||||
String res3 = HttpClientUtil.post(preUploadUrl, "{}", header2);
|
||||
JSONObject uploadObj = JSONObject.parseObject(res3);
|
||||
String upload_id = uploadObj.getString("upload_id");
|
||||
|
||||
//视频分片
|
||||
int chunkNumber = VideoDeviceUtil.device(videoPath, devicePath, chunk_size);
|
||||
if (chunkNumber != -1) {
|
||||
chunkNumber--;
|
||||
} else {
|
||||
throw new ChopperBotException("分割视频失败");
|
||||
}
|
||||
|
||||
//上传视频
|
||||
for (int i = 1; i <= chunkNumber; i++) {
|
||||
long end = i == chunkNumber ? filesize : i * chunk_size;
|
||||
long start = (i - 1) * chunk_size;
|
||||
File file = new File(devicePath + "chunk_" + i + ".bin");
|
||||
if (!file.exists()) {
|
||||
throw new ChopperBotException(String.format("未找到视频片段%s", i));
|
||||
}
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPut httpPut = new HttpPut(String.format("https:%s%s?partNumber=%s&uploadId=%s&chunk=%s&chunks=%s&size=%s&start=%s&end=%s&total=%s",
|
||||
endpoint, mp4_upos_uri.substring(mp4_upos_uri.indexOf('/') + 1), i, upload_id, i - 1, chunkNumber, end - start, start, end, filesize)); // 替换为实际的目标URL
|
||||
|
||||
byte[] binaryData = new byte[(int) file.length()];
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
fileInputStream.read(binaryData);
|
||||
fileInputStream.close();
|
||||
|
||||
ByteArrayEntity entity = new ByteArrayEntity(binaryData);
|
||||
httpPut.setEntity(entity);
|
||||
httpPut.setHeader("Content-Type", "application/octet-stream");
|
||||
httpPut.setHeader("X-Upos-Auth", auth);
|
||||
|
||||
CloseableHttpResponse response = httpClient.execute(httpPut);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
System.out.println("上传中:" + i * 100 / chunkNumber + "%");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//通知上传完成
|
||||
JSONObject body = new JSONObject();
|
||||
JSONArray parts = new JSONArray();
|
||||
for (int i = 1; i <= chunkNumber; i++) {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("partNumber", i);
|
||||
item.put("eTag", "etag");
|
||||
parts.add(item);
|
||||
}
|
||||
body.put("parts", parts);
|
||||
String res4 = HttpClientUtil.post(
|
||||
String.format(
|
||||
"https:%s%s?output=json&name=test.mp4%s&uploadId=%s&biz_id=%s",
|
||||
endpoint, mp4_upos_uri.substring(mp4_upos_uri.indexOf('/') + 1), put_query.substring(put_query.indexOf('&')), upload_id, biz_id
|
||||
), body.toString(), header2
|
||||
);
|
||||
|
||||
//上传封面
|
||||
String base64Image = "";
|
||||
try {
|
||||
File imageFile = new File(coverPath);
|
||||
FileInputStream fileInputStream = new FileInputStream(imageFile);
|
||||
byte[] imageData = new byte[(int) imageFile.length()];
|
||||
fileInputStream.read(imageData);
|
||||
fileInputStream.close();
|
||||
base64Image = Base64.getEncoder().encodeToString(imageData);
|
||||
base64Image = "data:image/jpeg;base64," + base64Image;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
List<NameValuePair> list = new ArrayList<>();
|
||||
list.add(new BasicNameValuePair("cover", base64Image));
|
||||
list.add(new BasicNameValuePair("csrf", csrf));
|
||||
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, UTF_8);
|
||||
String res5 = HttpClientUtil.post("https://member.bilibili.com/x/vu/web/cover/up?t=" + LocalTime.now(), urlEncodedFormEntity, header);
|
||||
JSONObject cover = JSONObject.parseObject(res5);
|
||||
JSONObject coverData = cover.getJSONObject("data");
|
||||
if (coverData == null) return;
|
||||
String coverUrl = coverData.getString("url");
|
||||
|
||||
//发布视频
|
||||
JSONObject body1 = new JSONObject();
|
||||
JSONObject subtitle = new JSONObject();
|
||||
JSONArray videos = new JSONArray();
|
||||
subtitle.put("open", 0);
|
||||
subtitle.put("lan", " ");
|
||||
JSONObject video = new JSONObject();
|
||||
video.put("filename", mp4_upos_uri.substring(mp4_upos_uri.lastIndexOf('/') + 1, mp4_upos_uri.indexOf('.')));
|
||||
video.put("title", "test");
|
||||
video.put("desc", "");
|
||||
video.put("cid", biz_id);
|
||||
videos.add(video);
|
||||
body1.put("act_reserve_create", 0);
|
||||
body1.put("copyright", 1);
|
||||
body1.put("cover", coverUrl); //封面
|
||||
body1.put("csrf", csrf);
|
||||
body1.put("desc", "");
|
||||
body1.put("desc_format_id", 0);
|
||||
body1.put("dolby", 0);
|
||||
body1.put("dynamic", "");
|
||||
body1.put("interactive", 0);
|
||||
body1.put("lossless_music", 0);
|
||||
body1.put("no_disturbance", 0);
|
||||
body1.put("no_reprint", 1);
|
||||
body1.put("recreate", -1);
|
||||
body1.put("tag", "助眠,音乐"); //视频标签
|
||||
body1.put("tid", 130); //视频分区 130:音乐综合
|
||||
body1.put("title", "test");
|
||||
body1.put("up_close_danmu", false);
|
||||
body1.put("up_close_reply", false);
|
||||
body1.put("up_selection_reply", false);
|
||||
body1.put("web_os", 1);
|
||||
body1.put("subtitle", subtitle);
|
||||
body1.put("videos", videos);
|
||||
String res6 = HttpClientUtil.post(String.format("https://member.bilibili.com/x/vu/web/add/v3?t=%s&csrf=%s", LocalTime.now(), csrf), body1.toString(), header);
|
||||
JSONObject res6Obj = JSONObject.parseObject(res6);
|
||||
int code = res6Obj.getInteger("code");
|
||||
if (code == 0) {
|
||||
System.out.println("发布成功");
|
||||
} else {
|
||||
throw new ChopperBotException("发布失败,请手动发布");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.example.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
/**
|
||||
* @author dhx
|
||||
* @date 2023/9/18 15:30
|
||||
*/
|
||||
public class FileUtil {
|
||||
/**
|
||||
* 获取文件大小
|
||||
*/
|
||||
public static int getFilesize(String filePath){
|
||||
int filesize = 0;
|
||||
FileInputStream fileInputStream = null;
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (file.exists() && file.isFile()) {
|
||||
fileInputStream = new FileInputStream(file);
|
||||
filesize = fileInputStream.available();
|
||||
fileInputStream.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
return filesize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package org.example.utils;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 简单的get,post请求工具类
|
||||
* @author 燧枫
|
||||
* @date 2023/5/16 19:24
|
||||
*/
|
||||
public class HttpClientUtil {
|
||||
|
||||
private static final CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
|
||||
public static String get(String url) {
|
||||
return executeRequest(new HttpGet(url));
|
||||
}
|
||||
|
||||
public static String get(String url, Map<String, String> headers) {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
addHeaders(httpGet, headers);
|
||||
return executeRequest(httpGet);
|
||||
}
|
||||
|
||||
public static String post(String url){
|
||||
return executeRequest(new HttpPost(url));
|
||||
}
|
||||
|
||||
public static String post(String url, String json) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
|
||||
return executeRequest(httpPost);
|
||||
}
|
||||
|
||||
public static String post(String url, String json, Map<String, String> headers) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
|
||||
addHeaders(httpPost, headers);
|
||||
return executeRequest(httpPost);
|
||||
}
|
||||
|
||||
public static String post(String url, UrlEncodedFormEntity urlEncodedFormEntity, Map<String, String> headers) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setEntity(urlEncodedFormEntity);
|
||||
addHeaders(httpPost, headers);
|
||||
return executeRequest(httpPost);
|
||||
}
|
||||
|
||||
public static String put(String url) {
|
||||
return executeRequest(new HttpPut(url));
|
||||
}
|
||||
|
||||
public static String put(String url, String json) {
|
||||
HttpPut httpPut = new HttpPut(url);
|
||||
httpPut.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
|
||||
return executeRequest(httpPut);
|
||||
}
|
||||
|
||||
public static String put(String url, String json, Map<String, String> headers) {
|
||||
HttpPut httpPut = new HttpPut(url);
|
||||
httpPut.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
|
||||
addHeaders(httpPut, headers);
|
||||
return executeRequest(httpPut);
|
||||
}
|
||||
|
||||
public static String delete(String url) {
|
||||
return executeRequest(new HttpDelete(url));
|
||||
}
|
||||
|
||||
public static String delete(String url, Map<String, String> headers) {
|
||||
HttpDelete httpDelete = new HttpDelete(url);
|
||||
addHeaders(httpDelete, headers);
|
||||
return executeRequest(httpDelete);
|
||||
}
|
||||
|
||||
private static String executeRequest(HttpUriRequest request) {
|
||||
try (CloseableHttpResponse response = httpClient.execute(request)) {
|
||||
return handleResponse(response);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addHeaders(HttpRequest httpRequest, Map<String, String> headers) {
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
httpRequest.addHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String handleResponse(HttpResponse response) {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
try {
|
||||
return EntityUtils.toString(entity, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Response entity is null");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.example.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 视频分段工具类
|
||||
* @author dhx
|
||||
* @date 2023/9/18 15:08
|
||||
*/
|
||||
public class VideoDeviceUtil {
|
||||
/**
|
||||
* videoPath: 视频存放路径
|
||||
* savePath: 分割后视频存放路径
|
||||
* chunk_size: 每一段大小
|
||||
* 返回值: 分为多少块
|
||||
*/
|
||||
public static int device(String videoPath,String savePath,Long chunk_size){
|
||||
int chunkNumber = 1;
|
||||
try {
|
||||
File inputFile = new File(videoPath);
|
||||
FileInputStream fileInputStream = new FileInputStream(inputFile);
|
||||
byte[] buffer = new byte[Math.toIntExact(chunk_size)];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
||||
String outputFilePath = savePath + "chunk_" + chunkNumber + ".bin";
|
||||
File outputFile = new File(outputFilePath);
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
|
||||
fileOutputStream.write(buffer, 0, bytesRead);
|
||||
fileOutputStream.close();
|
||||
chunkNumber++;
|
||||
}
|
||||
fileInputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
return chunkNumber;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user