1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-11-11 20:05:04 -05:00
filebot/source/net/filebot/web/ShooterSubtitles.java
2017-01-14 04:32:42 +08:00

219 lines
5.7 KiB
Java

package net.filebot.web;
import static java.nio.charset.StandardCharsets.*;
import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
import static net.filebot.util.FileUtilities.*;
import static net.filebot.util.JsonUtilities.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;
import javax.swing.Icon;
import net.filebot.Cache;
import net.filebot.CacheType;
import net.filebot.ResourceManager;
public class ShooterSubtitles implements VideoHashSubtitleService {
@Override
public String getName() {
return "射手网";
}
@Override
public Icon getIcon() {
return ResourceManager.getIcon("search.shooter");
}
@Override
public String getIdentifier() {
return "Shooter";
}
@Override
public URI getLink() {
return URI.create("http://shooter.cn");
}
public Cache getCache() {
return Cache.getCache(getName(), CacheType.Daily);
}
@Override
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] videoFiles, Locale locale) throws Exception {
Map<File, List<SubtitleDescriptor>> result = new HashMap<File, List<SubtitleDescriptor>>();
for (File it : videoFiles) {
result.put(it, getSubtitleList(it, locale));
}
return result;
}
protected URL getSubApiUrl() {
try {
return new URL(System.getProperty("net.filebot.web.ShooterSubtitles.url", "https://www.shooter.cn/api/subapi.php"));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
/**
* @see https://docs.google.com/document/d/1ufdzy6jbornkXxsD-OGl3kgWa4P9WO5NZb6_QYZiGI0/preview
*/
public synchronized List<SubtitleDescriptor> getSubtitleList(File file, Locale locale) throws Exception {
if (Stream.of(Locale.CHINESE, Locale.ENGLISH).noneMatch(l -> l.getLanguage().equals(locale.getLanguage()))) {
throw new IllegalArgumentException("Language not supported: " + locale);
}
if (file.length() < 8192) {
return emptyList();
}
URL endpoint = getSubApiUrl();
Map<String, String> param = new LinkedHashMap<String, String>();
param.put("filehash", computeFileHash(file));
param.put("pathinfo", file.getPath());
param.put("format", "json");
param.put("lang", Locale.CHINESE.getLanguage().equals(locale.getLanguage()) ? "Chn" : "Eng");
// use the first best option and ignore the rest
return getCache().castList(SubtitleDescriptor.class).computeIfAbsent(param.toString(), it -> {
ByteBuffer bb = WebRequest.post(endpoint, param, null);
// error response
if (bb.remaining() == 1 && bb.get(0) == -1)
return emptyList();
String name = getNameWithoutExtension(file.getName());
Object response = readJson(UTF_8.decode(bb));
return streamJsonObjects(response).flatMap(n -> streamJsonObjects(n, "Files")).map(f -> {
String type = getString(f, "Ext");
String link = getString(f, "Link");
return new ShooterSubtitleDescriptor(name, type, link, locale.getDisplayLanguage(Locale.ENGLISH));
}).limit(1).collect(toList());
});
}
@Override
public CheckResult checkSubtitle(File videoFile, File subtitleFile) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void uploadSubtitle(Object identity, Locale locale, File[] videoFile, File[] subtitleFile) throws Exception {
throw new UnsupportedOperationException();
}
/**
*
* @see https://docs.google.com/document/d/1w5MCBO61rKQ6hI5m9laJLWse__yTYdRugpVyz4RzrmM/preview
*/
protected static String computeFileHash(File file) throws IOException {
List<String> hashes = new ArrayList<String>();
long fileSize = file.length();
if (fileSize < 8192)
return "";
long[] offset = new long[4];
offset[3] = fileSize - 8192;
offset[2] = fileSize / 3;
offset[1] = fileSize / 3 * 2;
offset[0] = 4096;
try (RandomAccessFile f = new RandomAccessFile(file, "r")) {
byte[] buffer = new byte[4096];
for (int i = 0; i < 4; i++) {
f.seek(offset[i]);
int read = f.read(buffer, 0, buffer.length);
hashes.add(md5(buffer, 0, read));
}
}
return String.join(";", hashes);
}
protected static String md5(byte[] input, int offset, int len) {
try {
MessageDigest hash = MessageDigest.getInstance("MD5");
hash.update(input, offset, len);
return String.format("%032x", new BigInteger(1, hash.digest())); // as hex string
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static class ShooterSubtitleDescriptor implements SubtitleDescriptor, Serializable {
private String name;
private String type;
private String link;
private String language;
public ShooterSubtitleDescriptor(String name, String type, String link, String language) {
this.name = name;
this.type = type;
this.link = link;
this.language = language;
}
@Override
public String getName() {
return name;
}
@Override
public String getLanguageName() {
return language;
}
@Override
public String getType() {
return type;
}
@Override
public ByteBuffer fetch() throws Exception {
return WebRequest.fetch(new URL(link));
}
@Override
public String getPath() {
return getName() + "." + getType();
}
@Override
public long getLength() {
return -1;
}
@Override
public File toFile() {
return new File(getPath());
}
@Override
public String toString() {
return String.format("%s [%s]", getName(), getLanguageName());
}
}
}