Skip to content

Instantly share code, notes, and snippets.

@sinancetinkaya
Last active March 20, 2025 00:53
Show Gist options
  • Save sinancetinkaya/1d115e55b3a94cb305b125a62dde001a to your computer and use it in GitHub Desktop.
Save sinancetinkaya/1d115e55b3a94cb305b125a62dde001a to your computer and use it in GitHub Desktop.
PotPlayer extension. Searches subtitles for given `SublanguageIDs` only
/*
subtitle search by opensubtitle
*/
// void OnInitialize()
// void OnFinalize()
// string GetTitle() -> get title for UI
// string GetVersion -> get version for manage
// string GetDesc() -> get detail information
// string GetLoginTitle() -> get title for login dialog
// string GetLoginDesc() -> get desc for login dialog
// string GetUserText() -> get user text for login dialog
// string GetPasswordText() -> get password text for login dialog
// string ServerCheck(string User, string Pass) -> server check
// string ServerLogin(string User, string Pass) -> login
// void ServerLogout() -> logout
//------------------------------------------------------------------------------------------------
// string GetLanguages() -> get support language
// string SubtitleWebSearch(string MovieFileName, dictionary MovieMetaData) -> search subtitle bu web browser
// array<dictionary> SubtitleSearch(string MovieFileName, dictionary MovieMetaData) -> search subtitle
// string SubtitleDownload(string id) -> download subtitle
// string GetUploadFormat() -> upload format
// string SubtitleUpload(string MovieFileName, dictionary MovieMetaData, string SubtitleName, string SubtitleContent) -> upload subtitle
uint64 GetHash(string FileName, int64 &out size)
{
uint64 hash = 0;
uintptr fp = HostFileOpen(FileName);
size = 0;
if (fp != 0)
{
size = HostFileLength(fp);
hash = size;
for (int i = 0; i < 65536 / 8; i++) hash = hash + HostFileReadQWORD(fp);
int64 ep = size - 65536;
if (ep < 0) ep = 0;
HostFileSeek(fp, ep, 0);
for (int i = 0; i < 65536 / 8; i++) hash = hash + HostFileReadQWORD(fp);
HostFileClose(fp);
}
return hash;
}
string Token = "";
string ISDB_URL = "http://www.opensubtitles.org/isdb/";
string RPC_URL = "http://api.opensubtitles.org/xml-rpc";
array<dictionary> OldSubtitleSearch(string MovieFileName, dictionary MovieMetaData)
{
array<dictionary> ret;
string url = ISDB_URL + "index.php?player=mpc&";
int64 size = 0;
uint64 hash = GetHash(MovieFileName, size);
string title = string(MovieMetaData["title"]);
string param = "name[0]=" + HostUrlEncode(title) + "&hash[0]=" + formatUInt(hash, "0h", 16) + "&size[0]=" + formatUInt(size, "0h", 16);
url = url + param;
string text = HostUrlGetString(url, "MPC-HC");
if (!text.empty())
{
string ticket = "";
string movie = "";
string subtitle = "";
string name = "";
string language = "";
string iso639_2 = "";
string format = "";
string discs = "";
string disc_no = "";
string nick = "";
string email = "";
array<string> lines = text.split("\n");
for (int i = 0, len = lines.size(); i < len; i++)
{
string line = lines[i];
if (!line.empty())
{
int s = line.find("=");
if (s > 0)
{
string key = line.substr(0, s);
string value = line.substr(s + 1);
if (key == "ticket") ticket = value;
else if (key == "movie")
{
array<string> movies = value.split("|");
for (int j = 0, len = movies.size(); j < len; j++)
{
string m = movies[j];
if (!m.empty())
{
if (movie.empty()) movie = m;
else movie = movie + " ," + m;
}
}
}
else if (key == "subtitle") subtitle = value;
else if (key == "name") name = value;
else if (key == "discs") discs = value;
else if (key == "disc_no") disc_no = value;
else if (key == "format") format = value;
else if (key == "iso639_2") iso639_2 = value;
else if (key == "language") language = value;
else if (key == "nick") nick = value;
else if (key == "email") email = value;
}
bool IsAdd = false;
if (line == "endsubtitle") IsAdd = true;
else if (line == "endmovie") IsAdd = true;
else if (line == "end")
{
IsAdd = true;
break;
}
if (IsAdd && !subtitle.empty() && !movie.empty())
{
dictionary item;
string id = "id=" + subtitle + "&ticket=";
// id = id + ticket;
item["id"] = id;
item["title"] = movie;
if (!name.empty()) item["fileName"] = name;
if (!format.empty()) item["format"] = format;
if (!language.empty()) item["language"] = language;
if (!iso639_2.empty()) item["lang"] = iso639_2;
if (!discs.empty() && !disc_no.empty()) item["disc"] = disc_no + "/" + discs;
if (!nick.empty()) item["nick"] = nick;
if (!email.empty()) item["email"] = email;
subtitle = "";
name = "";
language = "";
iso639_2 = "";
disc_no = "";
discs = "";
nick = "";
email = "";
ret.insertLast(item);
}
}
}
}
return ret;
}
string OldSubtitleDownload(string id)
{
string url = ISDB_URL + "dl.php?" + id;
string text = HostUrlGetString(url, "MPC-HC");
return text;
}
// Function to get the file name without extension and path
string GetFileNameWithoutExtension(const string &in filePath)
{
// Find the last occurrence of the directory separator
int lastSlash = filePath.findLast("\\");
if (lastSlash == -1)
lastSlash = filePath.findLast("/");
// Extract the file name with extension
string fileNameWithExt = lastSlash == -1 ? filePath : filePath.substr(lastSlash + 1);
// Find the last occurrence of the dot (extension separator)
int lastDot = fileNameWithExt.findLast(".");
// If there's no dot, return the entire file name
if (lastDot == -1)
return fileNameWithExt;
// Extract the file name without the extension
return fileNameWithExt.substr(0, lastDot);
}
array<dictionary> NewSubtitleSearch(string MovieFileName, dictionary MovieMetaData)
{
array<dictionary> ret;
string MovieFileNameWithoutExt = GetFileNameWithoutExtension(MovieFileName);
string year = string(MovieMetaData["year"]);
string title = string(MovieMetaData["title"]);
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, res;
args[0] = Token;
if (!year.empty() && !title.empty()) {
args[1][0]["query"] = title + " " + year;
}
else {
int64 size = 0;
uint64 hash = GetHash(MovieFileName, size);
if (hash > 0) args[1][0]["moviehash"] = formatUInt(hash, "0h", 16);
if (size > 0) args[1][0]["moviebytesize"] = formatInt(size);
}
args[2]["limit"] = 500;
array<string> SublanguageIDs = { "eng", "tur"};
for (uint i = 0; i < SublanguageIDs.length(); i++)
{
args[1][0]["sublanguageid"] = SublanguageIDs[i];
if (xmlrpc.execute("SearchSubtitles", args, res) && res.isObject())
{
XmlRpcValue data = res["data"];
if (data.isArray())
{
for(int i = 0, len = data.size(); i < len; i++)
{
XmlRpcValue v = data[i];
if (v.isObject())
{
XmlRpcValue MovieName = v["MovieName"];
XmlRpcValue IDSubtitleFile = v["IDSubtitleFile"];
if (MovieName.canString() && IDSubtitleFile.canString())
{
dictionary item;
XmlRpcValue MovieYear = v["MovieYear"];
if (MovieYear.canString()) item["year"] = MovieYear.asString();
item["id"] = IDSubtitleFile.asString();
item["title"] = MovieName.asString();
XmlRpcValue SubFormat = v["SubFormat"];
if (SubFormat.isString()) item["format"] = SubFormat.asString();
XmlRpcValue SubFileName = v["SubFileName"];
//if (SubFileName.canString()) item["fileName"] = SubFileName.asString();
if (SubFileName.canString()) item["fileName"] = MovieFileNameWithoutExt + "." + v["LanguageName"].asString() + "." + SubFormat.asString();
XmlRpcValue LanguageName = v["LanguageName"];
if (LanguageName.isString()) item["language"] = LanguageName.asString();
XmlRpcValue ISO639 = v["ISO639"];
if (ISO639.isString()) item["lang"] = ISO639.asString();
XmlRpcValue SubSumCD = v["SubSumCD"];
XmlRpcValue SubActualCD = v["SubActualCD"];
if (SubSumCD.canString() && SubActualCD.canString()) item["disc"] = SubActualCD.asString() + "/" + SubSumCD.asString();
XmlRpcValue SubDownloadsCnt = v["SubDownloadsCnt"];
if (SubDownloadsCnt.canString()) item["downloadCount"] = SubDownloadsCnt.asString();
XmlRpcValue SeriesSeason = v["SeriesSeason"];
if (SeriesSeason.canString()) item["seasonNumber"] = SeriesSeason.asString();
XmlRpcValue SeriesEpisode = v["SeriesEpisode"];
if (SeriesEpisode.canString()) item["episodeNumber"] = SeriesEpisode.asString();
XmlRpcValue SubtitlesLink = v["SubtitlesLink"];
if (SubtitlesLink.canString()) item["url"] = SubtitlesLink.asString();
XmlRpcValue IDMovieImdb = v["IDMovieImdb"];
if (IDMovieImdb.canString()) item["imdb"] = IDMovieImdb.asString();
XmlRpcValue SubBad = v["SubBad"];
if (SubBad.canString()) item["isBad"] = SubBad.asString();
XmlRpcValue SubHearingImpaired = v["SubHearingImpaired"];
if (SubHearingImpaired.canString()) item["hearingImpaired"] = SubHearingImpaired.asString();
ret.insertLast(item);
}
}
}
}
}
}
return ret;
}
string NewSubtitleDownload(string id)
{
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, res;
args[0] = Token;
args[1][0] = id;
if (xmlrpc.execute("DownloadSubtitles", args, res) && res.isObject())
{
XmlRpcValue data = res["data"];
if (data.isArray())
{
XmlRpcValue sub = data[0];
if (sub.isObject())
{
XmlRpcValue bsse64 = sub["data"];
if (bsse64.isString()) return HostBase64Dec(bsse64.asString());
}
}
}
return "";
}
string GetTitle()
{
return "OpenSubtitles by Sinan";
}
string GetVersion()
{
return "1";
}
string GetDesc()
{
return "https://api.opensubtitles.org";
}
string GetLanguages()
{
string ret = "";
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, res;
args = "en";
if (xmlrpc.execute("GetSubLanguages", args, res) && res.isObject())
{
XmlRpcValue data = res["data"];
if (data.isArray())
{
for(int i = 0, len = data.size(); i < len; i++)
{
XmlRpcValue lang = data[i];
if (lang.isObject())
{
//XmlRpcValue SubLanguageID = lang["SubLanguageID"];
XmlRpcValue ISO639 = lang["ISO639"];
if (ISO639.canString())
{
string iso = ISO639.asString();
if (!iso.empty())
{
if (ret.empty()) ret = iso;
else ret = ret + "," + iso;
}
}
}
}
}
}
return ret;
}
string GetLoginTitle()
{
return "";
}
string GetLoginDesc()
{
return "";;
}
string ServerCheck(string User, string Pass)
{
// new
string result = ServerLogin(User, Pass);
if (!result.empty() && !Token.empty()) return result;
// old
string url = ISDB_URL + "test.php";
string text = HostUrlGetString(url);
string ret = "200 OK";
if (text != "ISDb v1")
{
int s = text.find("ISDb v");
if (s == 0) ret = "Invalid version";
else ret = text;
}
return ret;
}
string ServerLogin(string User, string Pass)
{
string ret = "";
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, res;
args[0] = User;
args[1] = Pass;
args[2] = "en";
args[3] = "VLsub 0.10.2";
if (xmlrpc.execute("LogIn", args, res) && res.isObject())
{
XmlRpcValue token = res["token"];
XmlRpcValue status = res["status"];
if (token.canString())
{
ServerLogout();
Token = token.asString();
}
if (status.canString()) return status.asString();
}
return "cannot connect";
}
void ServerLogout()
{
if (!Token.empty())
{
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, res;
args[0] = Token;
xmlrpc.execute("LogOut", args, res);
Token = "";
}
}
string SubtitleWebSearch(string MovieFileName, dictionary MovieMetaData)
{
string url = ISDB_URL + "index.php?";
int64 size = 0;
uint64 hash = GetHash(MovieFileName, size);
string title = string(MovieMetaData["title"]);
string param = "name[0]=" + HostUrlEncode(title) + "&hash[0]=" + formatUInt(hash, "0h", 16) + "&size[0]=" + formatUInt(size, "0h", 16);
return url + param;
}
array<dictionary> SubtitleSearch(string MovieFileName, dictionary MovieMetaData)
{
if (Token.empty()) return OldSubtitleSearch(MovieFileName, MovieMetaData);
return NewSubtitleSearch(MovieFileName, MovieMetaData);
}
string SubtitleDownload(string id)
{
if (Token.empty()) return OldSubtitleDownload(id);
return NewSubtitleDownload(id);
}
string GetUploadFormat()
{
return "srt";
}
string SubtitleUpload(string MovieFileName, dictionary MovieMetaData, string SubtitleName, string SubtitleContent)
{
if (!Token.empty())
{
int64 size = 0;
uint64 hash = GetHash(MovieFileName, size);
string MovieName = string(MovieMetaData["title"]);
XmlRpcClient xmlrpc(RPC_URL);
XmlRpcValue args, result;
string hashStr = formatUInt(hash, "0h", 16);
args[0] = Token;
args[1]["cd1"]["moviehash"] = hashStr;
args[1]["cd1"]["moviebytesize"] = formatUInt(size);
args[1]["cd1"]["subhash"] = HostHashMD5(SubtitleContent);
args[1]["cd1"]["subfilename"] = SubtitleName;
args[1]["cd1"]["moviefilename"] = MovieName;
if (xmlrpc.execute("TryUploadSubtitles", args, result) && result.isObject())
{
XmlRpcValue alreadyindb = result["alreadyindb"];
if (alreadyindb.isBool() && alreadyindb.asBool()) return "already exist";
else if (alreadyindb.isInt() && alreadyindb.asInt() != 0) return "already exist";
else
{
string imdb = "";
XmlRpcValue data = result["data"];
if (data.isArray())
{
XmlRpcValue data0 = data[0];
if (data0.isObject())
{
XmlRpcValue IDMovieImdb = data0["IDMovieImdb"];
if (IDMovieImdb.canString()) imdb = IDMovieImdb.asString();
}
}
if (imdb.empty()) // get imdb by CheckMovieHash
{
XmlRpcValue _args, _result;
_args[0] = Token;
_args[1][0] = hashStr;
if (xmlrpc.execute("CheckMovieHash", _args, _result) && _result.isObject())
{
XmlRpcValue data2 = _result["data"];
if (data2.isObject())
{
XmlRpcValue moviehash = data2[hashStr];
if (moviehash.isObject())
{
XmlRpcValue ServerName = moviehash["MovieName"];
if (ServerName.isString())
{
if (HostCompareMovieName(MovieName, ServerName.asString()))
{
XmlRpcValue IDMovieImdb = moviehash["IDMovieImdb"];
if (IDMovieImdb.canString()) imdb = IDMovieImdb.asString();
}
}
}
}
}
}
if (imdb.empty()) // get imdb by CheckMovieHash2
{
XmlRpcValue _args, _result;
_args[0] = Token;
_args[1][0] = hashStr;
if (xmlrpc.execute("CheckMovieHash2", _args, _result) && _result.isObject())
{
XmlRpcValue data2 = _result["data"];
if (data2.isObject())
{
XmlRpcValue moviehash = data2[hashStr];
if (moviehash.isArray())
{
for(int i = 0, len = moviehash.size(); i < len; i++)
{
XmlRpcValue v = moviehash[i];
if (v.isObject())
{
XmlRpcValue ServerName = v["MovieName"];
if (ServerName.isString())
{
if (HostCompareMovieName(MovieName, ServerName.asString()))
{
XmlRpcValue IDMovieImdb = moviehash["IDMovieImdb"];
if (IDMovieImdb.canString())
{
imdb = IDMovieImdb.asString();
break;
}
}
}
}
}
}
}
}
}
if (imdb.empty()) // get imdb by SearchMoviesOnIMDB
{
XmlRpcValue _args, _result;
_args[0] = Token;
_args[1] = MovieName;
if (xmlrpc.execute("SearchMoviesOnIMDB", _args, _result) && _result.isObject())
{
XmlRpcValue data2 = _result["data"];
if (data2.isArray())
{
for(int i = 0, len = data2.size(); i < len; i++)
{
XmlRpcValue v = data2[i];
if (v.isObject())
{
XmlRpcValue title = v["title"];
if (title.isString())
{
if (HostCompareMovieName(MovieName, title.asString()))
{
XmlRpcValue id = v["id"];
if (id.canString())
{
imdb = id.asString();
break;
}
}
}
}
}
}
}
}
if (!imdb.empty()) // if imdb is exist
{
XmlRpcValue _args, _result;
_args[0] = Token;
_args[1][0]["moviehash"] = hashStr;
_args[1][0]["moviebytesize"] = size;
_args[1][0]["imdbid"] = imdb;
_args[1][0]["subfilename"] = SubtitleName;
_args[1][0]["moviefilename"] = MovieName;
if (xmlrpc.execute("InsertMovieHash", _args, _result) && _result.isObject())
{
string gzip = HostBase64Enc(HostGzipCompress(SubtitleContent));
args[1]["cd1"]["subcontent"] = gzip;
args[1]["baseinfo"]["idmovieimdb"] = imdb;
if (xmlrpc.execute("UploadSubtitles", args, result) && result.isObject())
{
XmlRpcValue status = result["status"];
if (status.isString()) return status.asString();
return "upload subtitle fail";
}
return "upload subtitle error";
}
return "insert movie hash error";
}
return "cannot find imdb";
}
// return "try upload fail";
}
return "try upload error";
}
return "not login";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment