*Plugins: Fixes/Changes/Maintenance*

- FaceBookComGallery: improved detection of actually desired item
DiskYandexNet:
- added support for password protected folders RE ticket #LQFA3817-MCAK-6226FQNK
- added better detection- and errorhandling for "read only" files (= files where uploader has disabled official download button)
- added errorhandling for non-video 'read-only' files
- login in crawler if account is available to avoid running into anti bot blocks too often
- set last modified timestamp so that it will be set correctly even when non-original file is downloaded (e.g. stream download of non officially downloadable file)
- moved unneeded function into YandexAlbum
- refactored errorhandling
- added support to download read-only .txt files
- do not set FilePackage with name "unknown" on single file items so that Various package handling can do its job
- improved parsing of docviewer URLs
- set full relative path as package name in website- and API handling (removed inconsistency)
- match same items (internal link_id) via 'hash' as the previous set value was different depending on source (website/API)
- better error message for non downloadable read-only files

git-svn-id: svn://svn.jdownloader.org/jdownloader/trunk@49595 ebf7c1c2-ba36-0410-9fe8-c592906822b4

Former-commit-id: c2f3a89b3eb4fc5cb4c9851fa774e8e01815d7e0
This commit is contained in:
psp 2024-08-16 07:10:59 +00:00
parent 9db3f26a2e
commit 12599b3a8c
4 changed files with 829 additions and 451 deletions

View File

@ -19,17 +19,28 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;
import org.appwork.storage.TypeRef;
import org.appwork.utils.StringUtils;
import org.appwork.utils.encoding.URLEncode;
import org.appwork.utils.parser.UrlQuery;
import org.jdownloader.scripting.JavaScriptEngineFactory;
import jd.PluginWrapper;
import jd.controlling.AccountController;
import jd.controlling.ProgressController;
import jd.http.Browser;
import jd.http.requests.PostRequest;
import jd.nutils.encoding.Encoding;
import jd.parser.Regex;
import jd.plugins.Account;
import jd.plugins.CryptedLink;
import jd.plugins.DecrypterException;
import jd.plugins.DecrypterPlugin;
import jd.plugins.DecrypterRetryException;
import jd.plugins.DecrypterRetryException.RetryReason;
@ -42,12 +53,6 @@ import jd.plugins.PluginForDecrypt;
import jd.plugins.components.PluginJSonUtils;
import jd.plugins.hoster.DiskYandexNet;
import org.appwork.storage.TypeRef;
import org.appwork.utils.StringUtils;
import org.appwork.utils.encoding.URLEncode;
import org.appwork.utils.parser.UrlQuery;
import org.jdownloader.scripting.JavaScriptEngineFactory;
@DecrypterPlugin(revision = "$Revision$", interfaceVersion = 3, names = {}, urls = {})
public class DiskYandexNetFolder extends PluginForDecrypt {
public DiskYandexNetFolder(PluginWrapper wrapper) {
@ -96,82 +101,166 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
}
/** Usually docviewer.yandex.xy but we're supporting so many domains and subdomains that a generic RegEx works better. */
private static final String type_docviewer = "(?i)https?://[^/]+/\\?url=ya\\-disk\\-public%3A%2F%2F([^/\"\\&]+).*?";
private final String type_primaryURLs = "(?i).+?public/?(\\?hash=.+|#.+)";
private final String type_shortURLs_d = "(?i)https?://[^/]+/d/[A-Za-z0-9\\-_]+((/[^/]+){0,})";
private final String type_shortURLs_i = "(?i)https?://[^/]+/i/[A-Za-z0-9\\-_]+";
private final String type_yadi_sk_mail = "(?i)https?://[^/]+/mail/\\?hash=.+";
private static final String JSON_TYPE_DIR = "dir";
private static final Pattern type_docviewer = Pattern.compile("https?://[^/]+/\\?url=ya\\-disk\\-public%3A%2F%2F([^/\"\\&]+).*?", Pattern.CASE_INSENSITIVE);
private final String type_primaryURLs = "(?i).+?public/?(\\?hash=.+|#.+)";
private final String type_shortURLs_d = "(?i)https?://[^/]+/d/[A-Za-z0-9\\-_]+((/[^/]+){0,})";
private final String type_shortURLs_i = "(?i)https?://[^/]+/i/[A-Za-z0-9\\-_]+";
private final String type_yadi_sk_mail = "(?i)https?://[^/]+/mail/\\?hash=.+";
private static final String JSON_TYPE_DIR = "dir";
private DiskYandexNet hosterplugin = null;
/** Using API: https://tech.yandex.ru/disk/api/reference/public-docpage/ */
@SuppressWarnings({ "deprecation" })
public ArrayList<DownloadLink> decryptIt(CryptedLink param, ProgressController progress) throws Exception {
public ArrayList<DownloadLink> decryptIt(final CryptedLink param, ProgressController progress) throws Exception {
final Account account = AccountController.getInstance().getValidAccount(this.getHost());
return decryptIt(param, account, true);
}
public ArrayList<DownloadLink> decryptIt(final CryptedLink param, final Account account, final boolean allowPagination) throws Exception {
br.setFollowRedirects(true);
/* Load hosterplugin to use same browser headers/settings. */
this.getNewPluginForHostInstance(this.getHost());
String contenturl = param.getCryptedUrl();
/* Do some URL corrections */
if (param.getCryptedUrl().matches(type_docviewer)) {
/* Documents in web view mode --> File-URLs! */
/* First lets fix broken URLs by removing unneeded parameters ... */
String tmp = param.getCryptedUrl();
final String remove = new Regex(tmp, "(\\&[a-z0-9]+=.+)").getMatch(0);
if (remove != null) {
tmp = tmp.replace(remove, "");
}
String hash = new Regex(tmp, type_docviewer).getMatch(0);
if (StringUtils.isEmpty(hash)) {
hash = new Regex(tmp, "url=ya\\-disk\\-public%3A%2F%2F(.+)").getMatch(0);
if (StringUtils.isEmpty(hash)) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
}
final Regex docviewer = new Regex(contenturl, type_docviewer);
if (docviewer.patternFind()) {
/* Documents in web view mode --> Change them to normal file-URLs! */
final UrlQuery query = UrlQuery.parse(contenturl);
final String param_url = query.get("url");
final String hash = param_url.replace("ya-disk-public%3A%2F%2F", "");
final String hashRoot = URLDecoder.decode(hash, "UTF-8");
contenturl = generateContentURL(hashRoot);
}
/* Load hosterplugin to use same browser headers/settings. */
hosterplugin = (DiskYandexNet) this.getNewPluginForHostInstance(this.getHost());
/* Login whenever possible. Helps us get around anti bot captchas. */
if (account != null) {
hosterplugin.login(account, false);
}
/**
* 2021-02-09: New: Prefer website if we do now know whether we got a file or a folder! API will fail in case it is a single file &&
* 2021-02-09: New: Prefer website if we don't know whether we got a file or a folder! API will fail in case it is a single file &&
* is currently quota-limited!
*/
if (StringUtils.isEmpty(this.getAdoptedCloudFolderStructure()) || StringUtils.isEmpty(getHashFromURL(contenturl))) {
/*
* Set password from previous CryptedLink e.g. password has been obtained when root folder was added so now we know it for all
* subfolders.
*/
String passToken = null;
final DownloadLink parent = param.getDownloadLink();
if (parent != null) {
/*
* 2024-08-13: psp: Password protected links cannot be processed via API in JDownloader [yet]. Reason: I was unable to find out
* which parameter the password needs to be sent as.
*/
passToken = parent.getStringProperty(DiskYandexNet.PROPERTY_PASSWORD_TOKEN);
}
if (StringUtils.isEmpty(this.getAdoptedCloudFolderStructure()) || StringUtils.isEmpty(getHashFromURL(contenturl)) || passToken != null) {
logger.info("Using website crawler because we cannot know whether we got a single file- or a folder");
return this.crawlFilesFoldersWebsite(param, contenturl);
return this.crawlFilesFoldersWebsite(param, contenturl, allowPagination);
} else {
logger.info("Using API crawler");
return this.crawlFilesFoldersAPI(param, contenturl);
return this.crawlFilesFoldersAPI(param, contenturl, allowPagination);
}
}
private ArrayList<DownloadLink> crawlFilesFoldersWebsite(final CryptedLink param, final String contenturl) throws Exception {
private ArrayList<DownloadLink> crawlFilesFoldersWebsite(final CryptedLink param, final String contenturl, final boolean allowPagination) throws Exception {
final ArrayList<DownloadLink> ret = new ArrayList<DownloadLink>();
String relativeDownloadPath = this.getAdoptedCloudFolderStructure();
if (relativeDownloadPath == null) {
relativeDownloadPath = "";
}
/*
* Set password from previous CryptedLink e.g. password has been obtained when root folder was added so now we know it for all
* subfolders.
*/
String passCode = null;
String passToken = null;
final DownloadLink parent = param.getDownloadLink();
if (parent != null) {
passToken = parent.getStringProperty(DiskYandexNet.PROPERTY_PASSWORD_TOKEN);
passCode = parent.getDownloadPassword();
if (passToken != null) {
/*
* If that token is still valid, it will grant us access to password protected folders without the need to send the password
* again for each subfolder crawl process.
*/
setFolderPasswordTokenCookie(br, Browser.getHost(contenturl), passToken);
}
}
/**
* 2021-02-12: If a user adds an URL leading to only one file but this file is part of a folder, this crawler will crawl everything
* from the root folder on. This is because the fiule could e.g. be at the end of a paginated folder -> Browser will do pagination
* from the root folder on. This is because the file could e.g. be at the end of a paginated folder -> Browser will do pagination
* until file is found but this takes too much time and effort for us so we'll just add everything. The user can then sort/find that
* file in the LinkGrabber.
*/
br.getPage(contenturl);
if (isOfflineWebsite(this.br)) {
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
}
this.checkErrors(br);
final String sk = DiskYandexNet.getSK(this.br);
final String json = br.getRegex("<script type=\"application/json\"[^>]*id=\"store-prefetch\"[^>]*>(.*?)</script>").getMatch(0);
Map<String, Object> map = restoreFromString(json, TypeRef.MAP);
Map<String, Object> json_root = null;
Map<String, Object> resources_map = null;
String sk = null;
boolean passwordSuccess = false;
folderPasswordVerificationLoop: do {
br.getPage(contenturl);
if (isOfflineWebsite(this.br)) {
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
}
this.hosterplugin.checkErrorsWebsite(br, parent, null);
sk = DiskYandexNet.getSK(this.br);
final String json = br.getRegex("<script type=\"application/json\"[^>]*id=\"store-prefetch\"[^>]*>(.*?)</script>").getMatch(0);
if (json == null) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
json_root = restoreFromString(json, TypeRef.MAP);
resources_map = (Map<String, Object>) json_root.get("resources");
final Map<String, Object> password_protected_map = (Map<String, Object>) resources_map.get("password-protected");
if (password_protected_map == null) {
/* No password needed or password has already been entered. */
break folderPasswordVerificationLoop;
} else if (passwordSuccess) {
/* Correct password has already been entered but is needed again here -> Something went really wrong */
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
/* Password protected folder */
Map<String, Object> pwresp = null;
pwloop: for (int i = 0; i <= 5; i++) {
if (StringUtils.isEmpty(passCode) || i > 0) {
/* Ask user for password */
passCode = getUserInput("Password?", param);
}
final Map<String, Object> postData = new HashMap<String, Object>();
postData.put("hash", password_protected_map.get("hash"));
postData.put("password", passCode);
postData.put("sk", sk);
final PostRequest request = br.createJSonPostRequest("/public/api/check-password", postData);
prepareJsonRequest(request, br);
br.getPage(request);
pwresp = hosterplugin.checkErrorsWebAPI(br, parent, null);
final Object errorO = pwresp.get("error");
if (errorO == null || Boolean.FALSE.equals(errorO)) {
logger.info("Pwloop: " + i + " | Correct password: " + passCode);
passwordSuccess = true;
break pwloop;
} else {
/* {"error":true,"statusCode":403,"code":"HTTP_403","data":{"code":309,"title":"Symlink invalid password"}} */
logger.info("Pwloop: " + i + " | Incorrect password: " + passCode);
continue pwloop;
}
}
if (!passwordSuccess) {
throw new DecrypterException(DecrypterException.PASSWORD);
}
passToken = pwresp.get("token").toString();
setFolderPasswordTokenCookie(br, br.getHost(), passToken);
/* Continue so page gets reloaded so we can get the "json without folder-password-prompt". */
logger.info("Performing page reload after successful password handling");
continue;
} while (true);
final String authSk = PluginJSonUtils.getJson(this.br, "authSk");
// final String rootResourceId = (String) map.get("rootResourceId");
final String currentResourceId = (String) map.get("currentResourceId");
Map<String, Object> entries = (Map<String, Object>) map.get("resources");
final String currentResourceId = (String) json_root.get("currentResourceId");
/*
* First find the base folder name: If there are multiple items as part of a folder, the first item is kind of a dummy item
* containing the name of the root folder.
*/
String hashMain = null;
for (final String key : entries.keySet()) {
final Map<String, Object> ressource = (Map<String, Object>) entries.get(key);
for (final String key : resources_map.keySet()) {
final Map<String, Object> ressource = (Map<String, Object>) resources_map.get(key);
final String type = (String) ressource.get("type");
if ("dir".equals(type) && ressource.get("parent") == null) {
hashMain = (String) ressource.get("hash");
@ -179,8 +268,8 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
}
}
String baseFolderName = null;
for (final String key : entries.keySet()) {
final Map<String, Object> ressource = (Map<String, Object>) entries.get(key);
for (final String key : resources_map.keySet()) {
final Map<String, Object> ressource = (Map<String, Object>) resources_map.get(key);
final String type = (String) ressource.get("type");
final String id = (String) ressource.get("id");
if ("dir".equals(type) && StringUtils.equals(id, currentResourceId)) {
@ -188,49 +277,48 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
break;
}
}
if (StringUtils.isEmpty(baseFolderName)) {
/* Fallback */
baseFolderName = "unknown";
}
if (StringUtils.isEmpty(relativeDownloadPath)) {
if (StringUtils.isEmpty(relativeDownloadPath) && baseFolderName != null) {
/* First time crawl of a possible folder structure -> Define root dir name */
relativeDownloadPath = baseFolderName;
}
final FilePackage fp = FilePackage.getInstance();
fp.setName(baseFolderName);
List<Object> ressources = new ArrayList<Object>();
/* First collect map items in list because API will return list later! */
for (final String key : entries.keySet()) {
ressources.add(entries.get(key));
FilePackage fp = null;
if (relativeDownloadPath != null) {
fp = FilePackage.getInstance();
fp.setName(relativeDownloadPath);
}
List<Object> ressources_list = new ArrayList<Object>();
/* First collect map items in list because API will return list later. */
for (final String key : resources_map.keySet()) {
ressources_list.add(resources_map.get(key));
}
final int maxItemsPerPage = 40;
int page = 0;
int page = 1;
int offset = 0;
do {
logger.info("Crawling page: " + (page + 1));
pagination: do {
logger.info("Crawling page: " + page);
boolean completed = false;
for (final Object ressourceO : ressources) {
for (final Object ressourceO : ressources_list) {
final Map<String, Object> ressource = (Map<String, Object>) ressourceO;
final String type = (String) ressource.get("type");
final String name = (String) ressource.get("name");
String hash = (String) ressource.get("hash");
final String path = (String) ressource.get("path");
final DownloadLink link;
if (type.equalsIgnoreCase("dir")) {
/* Folder */
final List<Object> children = (List<Object>) ressource.get("children");
if ((children != null && !children.isEmpty()) || path == null || StringUtils.equals(path, hashMain + ":")) {
/* Skip dummy entries - also do not increase out offset value! */
/* Skip dummy entries - also do not increase our offset value! */
continue;
}
offset += 1;
/* Subfolders go back into our decrypter! Path contains "<hash_long_decoded>:/path" */
/* Subfolders go back into our crawler! Path contains "<hash_long_decoded>:/path" */
final String folderlink = this.generateContentURL(path);
final DownloadLink dl = createDownloadlink(folderlink);
dl.setRelativeDownloadFolderPath(relativeDownloadPath + "/" + name);
ret.add(dl);
distribute(dl);
link = createDownloadlink(folderlink);
if (relativeDownloadPath != null) {
link.setRelativeDownloadFolderPath(relativeDownloadPath + "/" + name);
}
} else {
offset += 1;
// final Map<String, Object> ressourceMeta = (Map<String, Object>) ressource.get("meta");
/* File */
// final boolean antiFileSharing = ((Boolean) ressourceMeta.get("antiFileSharing")).booleanValue();
final String resource_id = (String) ressource.get("id");
if (StringUtils.isEmpty(hash) && !StringUtils.isEmpty(path) && path.contains(":/")) {
@ -240,8 +328,8 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(hash) || StringUtils.isEmpty(resource_id)) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
final DownloadLink dl = createDownloadlink("http://yandexdecrypted.net/" + System.currentTimeMillis() + new Random().nextInt(10000000));
parseFilePropertiesWebsite(dl, ressource);
link = createDownloadlink("http://yandexdecrypted.net/" + System.currentTimeMillis() + new Random().nextInt(10000000));
parseFilePropertiesWebsite(link, ressource);
/*
* We want the user to have an URL which he can open via browser and it does not only open up the root of the folder but
* the exact file he wants to have!
@ -253,75 +341,91 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
} else {
urlContent = "https://disk.yandex.com/public?hash=" + URLEncode.encodeURIComponent(hash);
}
if (!StringUtils.isEmpty(path)) {
/* Path contains hash + path */
dl.setProperty(DiskYandexNet.PROPERTY_HASH, path);
} else {
/* Hash only */
dl.setProperty(DiskYandexNet.PROPERTY_HASH, hash);
link.setProperty("mainlink", urlContent);
link.setContentUrl(urlContent);
if (fp != null) {
link._setFilePackage(fp);
}
dl.setProperty("mainlink", urlContent);
dl.setContentUrl(urlContent);
dl.setProperty(DiskYandexNet.PROPERTY_INTERNAL_FUID, resource_id);
if (ressources.size() > 1) {
if (StringUtils.isNotEmpty(relativeDownloadPath)) {
dl.setRelativeDownloadFolderPath(relativeDownloadPath);
}
dl._setFilePackage(fp);
if (relativeDownloadPath != null) {
link.setRelativeDownloadFolderPath(relativeDownloadPath);
}
ret.add(dl);
distribute(dl);
}
if (!StringUtils.isEmpty(path)) {
/* Path contains hash + path */
link.setProperty(DiskYandexNet.PROPERTY_HASH, path);
} else {
/* Hash only */
link.setProperty(DiskYandexNet.PROPERTY_HASH, hash);
}
if (passCode != null && passToken != null) {
link.setPasswordProtected(true);
link.setDownloadPassword(passCode);
link.setProperty(DiskYandexNet.PROPERTY_PASSWORD_TOKEN, passToken);
}
if (authSk != null) {
link.setProperty(DiskYandexNet.PROPERTY_LAST_AUTH_SK, authSk);
}
ret.add(link);
distribute(link);
offset += 1;
}
if (page > 0) {
completed = Boolean.TRUE.equals(entries.get("completed"));
if (page > 1) {
completed = Boolean.TRUE.equals(resources_map.get("completed"));
} else {
if (!completed) {
completed = ressources.size() < maxItemsPerPage;
completed = ressources_list.size() < maxItemsPerPage;
}
}
logger.info("Crawled page: " + page + " | Found items so far: " + ret.size());
if (completed) {
logger.info("Stopping because: Reached last page");
break;
break pagination;
} else if (StringUtils.isEmpty(sk)) {
/* This should never happen */
logger.warning("Pagination failure: sk missing");
break;
break pagination;
} else if (StringUtils.isEmpty(hashMain)) {
/* This should never happen */
logger.warning("Pagination failure: hashMain missing");
break;
break pagination;
} else if (!allowPagination) {
logger.info("Stopping because: Pagination is not allowed");
break pagination;
} else if (this.isAbort()) {
logger.info("Stopping because: Aborted by user");
break pagination;
}
// final Map<String, Object> paginationMap = new HashMap<String, Object>();
// final Map<String, Object> paginationOptions = new HashMap<String, Object>();
// paginationOptions.put("hasExperimentVideoWithoutPreview", true);
// paginationMap.put("hash", hash_long_decoded);
// paginationMap.put("offset", offset);
// paginationMap.put("withSizes", true);
// paginationMap.put("sk", sk);
// paginationMap.put("options", paginationOptions);
PostRequest request = br.createPostRequest("/public/api/fetch-list", (UrlQuery) null, null);
request.getHeaders().put("X-Requested-With", "XMLHttpRequest");
// request.getHeaders().put("X-Retpath-Y", param.getCryptedUrl());
request.getHeaders().put("Accept", "*/*");
request.getHeaders().put("Origin", "https://" + br._getURL().getHost());
request.setContentType("text/plain");
/* Continue to next page */
final PostRequest request = br.createPostRequest("/public/api/fetch-list", (UrlQuery) null, null);
prepareJsonRequest(request, br);
request.setPostDataString("%7B%22hash%22%3A%22" + Encoding.urlEncode(hashMain) + "%3A%22%2C%22offset%22%3A" + offset + "%2C%22withSizes%22%3Atrue%2C%22sk%22%3A%22" + sk + "%22%2C%22options%22%3A%7B%22hasExperimentVideoWithoutPreview%22%3Atrue%7D%7D");
br.getPage(request);
this.checkErrors(br);
entries = restoreFromString(br.getRequest().getHtmlCode(), TypeRef.MAP);
ressources = (List<Object>) entries.get("resources");
if (ressources == null) {
resources_map = hosterplugin.checkErrorsWebAPI(br, parent, null);
ressources_list = (List<Object>) resources_map.get("resources");
if (ressources_list == null) {
/* This should never happen */
logger.warning("Pagination failure: ressources missing");
break;
break pagination;
}
page += 1;
continue pagination;
} while (!this.isAbort());
return ret;
}
private ArrayList<DownloadLink> crawlFilesFoldersAPI(final CryptedLink param2, final String contenturl) throws Exception {
public static void setFolderPasswordTokenCookie(final Browser br, final String domain, final String token) {
br.setCookie(domain, "passToken", token);
}
private void prepareJsonRequest(final PostRequest request, final Browser originbr) {
request.getHeaders().put("X-Requested-With", "XMLHttpRequest");
// request.getHeaders().put("X-Retpath-Y", param.getCryptedUrl());
request.getHeaders().put("Accept", "*/*");
request.getHeaders().put("Origin", "https://" + originbr._getURL().getHost());
request.setContentType("text/plain");
}
private ArrayList<DownloadLink> crawlFilesFoldersAPI(final CryptedLink param, final String contenturl, final boolean allowPagination) throws Exception {
final ArrayList<DownloadLink> ret = new ArrayList<DownloadLink>();
String relativeDownloadPath = this.getAdoptedCloudFolderStructure();
if (relativeDownloadPath == null) {
@ -332,7 +436,7 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
hashWithPath = getHashFromURL(contenturl);
} else if (contenturl.matches(type_shortURLs_d) || contenturl.matches(type_shortURLs_i)) {
br.getPage(contenturl);
this.checkErrors(br);
this.hosterplugin.checkErrorsWebsite(br, null, null);
if (isOfflineWebsite(this.br)) {
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
}
@ -368,14 +472,18 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
}
this.br.getHeaders().put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
this.br.getHeaders().put("X-Requested-With", "XMLHttpRequest");
short offset = 0;
int offset = 0;
final short entries_per_request = 200;
int totalNumberofEntries = 0;
final FilePackage fp = FilePackage.getInstance();
do {
br.getPage(DiskYandexNet.APIV1_BASE + "/disk/public/resources?limit=" + entries_per_request + "&offset=" + offset + "&public_key=" + URLEncode.encodeURIComponent(hashWithoutPath) + "&path=" + URLEncode.encodeURIComponent(internalPath));
this.checkErrors(br);
Map<String, Object> entries = restoreFromString(br.getRequest().getHtmlCode(), TypeRef.MAP);
final UrlQuery query = new UrlQuery();
query.add("limit", Integer.toString(entries_per_request));
query.add("public_key", URLEncode.encodeURIComponent(hashWithoutPath));
query.add("path", URLEncode.encodeURIComponent(internalPath));
pagination: do {
query.addAndReplace("offset", Integer.toString(offset));
br.getPage(DiskYandexNet.APIV1_BASE + "/disk/public/resources?" + query.toString());
Map<String, Object> entries = this.hosterplugin.checkErrorsWebAPI(br, null, null);
/*
* 2021-01-19:
* {"message":"Не удалось найти запрошенный ресурс.","description":"Resource not found.","error":"DiskNotFoundError"}
@ -386,16 +494,16 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
final String type_main = (String) entries.get("type");
if (!type_main.equals(JSON_TYPE_DIR)) {
/* We only have a single file --> Add to downloadliste / host plugin */
final DownloadLink dl = parseSingleFileAPI(entries);
final DownloadLink link = parseSingleFileAPI(entries);
if (StringUtils.isNotEmpty(relativeDownloadPath)) {
dl.setRelativeDownloadFolderPath(relativeDownloadPath);
link.setRelativeDownloadFolderPath(relativeDownloadPath);
}
dl._setFilePackage(fp);
ret.add(dl);
link._setFilePackage(fp);
ret.add(link);
return ret;
}
final String walk_string = "_embedded/items";
final List<Object> resource_data_list = (List) JavaScriptEngineFactory.walkJson(entries, walk_string);
final List<Map<String, Object>> resource_data_list = (List<Map<String, Object>>) JavaScriptEngineFactory.walkJson(entries, walk_string);
if (offset == 0) {
/* Set total number of entries on first loop. */
final Map<String, Object> itemInfo = (Map<String, Object>) entries.get("_embedded");
@ -405,11 +513,11 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
baseFolderName = hashWithPath;
}
totalNumberofEntries = ((Number) itemInfo.get("total")).intValue();
fp.setName(baseFolderName);
if (StringUtils.isEmpty(relativeDownloadPath)) {
/* First time crawl of a possible folder structure -> Define root dir name */
relativeDownloadPath = baseFolderName;
}
fp.setName(relativeDownloadPath);
if (totalNumberofEntries == 0) {
if (!StringUtils.isEmpty(relativeDownloadPath)) {
throw new DecrypterRetryException(RetryReason.EMPTY_FOLDER, hashWithoutPath + "_" + relativeDownloadPath);
@ -418,62 +526,58 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
}
}
}
for (final Object list_object : resource_data_list) {
entries = (Map<String, Object>) list_object;
final String type = (String) entries.get("type");
final String hash = (String) entries.get("public_key");
final String path = (String) entries.get("path");
final String name = (String) entries.get("name");
for (final Map<String, Object> resource_data : resource_data_list) {
final String type = (String) resource_data.get("type");
final String hash = (String) resource_data.get("public_key");
final String path = (String) resource_data.get("path");
final String name = (String) resource_data.get("name");
if (StringUtils.isEmpty(type_main) || StringUtils.isEmpty(path) || StringUtils.isEmpty(name)) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
final DownloadLink link;
if (type.equals(JSON_TYPE_DIR)) {
/* Subfolders go back into our decrypter! */
/* Folder */
final String folderlink = "https://disk.yandex.com/public?hash=" + URLEncode.encodeURIComponent(hash + ":" + path);
final DownloadLink dl = createDownloadlink(folderlink);
dl.setRelativeDownloadFolderPath(relativeDownloadPath + "/" + name);
ret.add(dl);
} else {
final DownloadLink dl = parseSingleFileAPI(entries);
if (StringUtils.isNotEmpty(relativeDownloadPath)) {
dl.setRelativeDownloadFolderPath(relativeDownloadPath);
link = createDownloadlink(folderlink);
if (!StringUtils.isEmpty(relativeDownloadPath)) {
link.setRelativeDownloadFolderPath(relativeDownloadPath + "/" + name);
}
dl._setFilePackage(fp);
ret.add(dl);
distribute(dl);
} else {
/* File */
link = parseSingleFileAPI(resource_data);
if (!StringUtils.isEmpty(relativeDownloadPath)) {
link.setRelativeDownloadFolderPath(relativeDownloadPath);
}
link._setFilePackage(fp);
}
ret.add(link);
distribute(link);
offset++;
}
if (resource_data_list.size() < entries_per_request) {
/* Fail safe */
logger.info("Stopping because current page contains less items than max. items allowed --> Should be the last page");
break;
logger.info("Stopping because current page contains less items than max items per page --> Should be the last page");
break pagination;
} else if (offset >= totalNumberofEntries) {
logger.info("Stopping because: Reached end");
break;
break pagination;
} else if (!allowPagination) {
logger.info("Stopping because: Pagination is not allowed");
break pagination;
}
} while (!this.isAbort());
return ret;
}
private void checkErrors(final Browser br) throws PluginException, DecrypterRetryException {
if (br.getHttpConnection().getResponseCode() == 404) {
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
} else if (br.getURL().contains("/showcaptcha")) {
throw new DecrypterRetryException(RetryReason.HOST_RATE_LIMIT);
}
}
private DownloadLink parseSingleFileAPI(final Map<String, Object> entries) throws Exception {
final String hash = (String) entries.get("public_key");
final String path = (String) entries.get("path");
final String name = (String) entries.get("name");
final String resource_id = (String) entries.get("resource_id");
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(path) || StringUtils.isEmpty(hash) || StringUtils.isEmpty(resource_id)) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(path) || StringUtils.isEmpty(hash)) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
final DownloadLink dl = createDownloadlink("http://yandexdecrypted.net/" + System.currentTimeMillis() + new Random().nextInt(10000000));
parseFilePropertiesAPI(dl, entries);
final DownloadLink link = createDownloadlink("http://yandexdecrypted.net/" + System.currentTimeMillis() + new Random().nextInt(10000000));
parseFilePropertiesAPI(link, entries);
final String hashFull = hash + ":" + path;
final String urlContent = generateContentURL(hashFull);
/**
@ -492,14 +596,13 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
// */
// urlUser = urlContent;
// }
dl.setProperty("mainlink", generateContentURL(hashFull));
dl.setContentUrl(urlContent);
dl.setProperty(DiskYandexNet.PROPERTY_INTERNAL_FUID, resource_id);
return dl;
link.setProperty("mainlink", generateContentURL(hashFull));
link.setContentUrl(urlContent);
return link;
}
private String generateContentURL(final String hash) {
return "https://disk.yandex.com/public/?hash=" + URLEncode.encodeURIComponent(hash);
private String generateContentURL(final String hashWithPath) {
return "https://disk.yandex.com/public/?hash=" + URLEncode.encodeURIComponent(hashWithPath);
}
public static String getPathFromHash(final String hash) {
@ -527,25 +630,16 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
return br;
}
public static Map<String, Object> findModel(final List<Object> modelObjects, final String targetModelName) {
Map<String, Object> entries = null;
boolean foundResourceModel = false;
for (final Object modelo : modelObjects) {
entries = (Map<String, Object>) modelo;
final String model = (String) entries.get("model");
if (targetModelName.equalsIgnoreCase(model)) {
foundResourceModel = true;
break;
}
}
if (!foundResourceModel) {
return null;
}
return entries;
}
public static boolean isOfflineWebsite(final Browser br) {
return br.containsHTML("class=\"not\\-found\\-public__caption\"|class=\"error__icon error__icon_blocked\"|_file\\-blocked\"|A complaint was received regarding this file|>File blocked<") || br.getHttpConnection().getResponseCode() == 404 || br.getHttpConnection().getResponseCode() == 500;
if (br.containsHTML("class=\"not\\-found\\-public__caption\"|class=\"error__icon error__icon_blocked\"|_file\\-blocked\"|A complaint was received regarding this file|>\\s*File blocked\\s*<")) {
return true;
} else if (br.getHttpConnection().getResponseCode() == 404) {
return true;
} else if (br.getHttpConnection().getResponseCode() == 500) {
return true;
} else {
return false;
}
}
private String getHashFromURL(final String url) throws UnsupportedEncodingException, MalformedURLException {
@ -557,13 +651,13 @@ public class DiskYandexNetFolder extends PluginForDecrypt {
}
}
private void parseFilePropertiesAPI(final DownloadLink dl, final Map<String, Object> entries) throws Exception {
final AvailableStatus status = DiskYandexNet.parseInformationAPIAvailablecheckFiles(this, dl, null, entries);
dl.setAvailableStatus(status);
private void parseFilePropertiesAPI(final DownloadLink link, final Map<String, Object> entries) throws Exception {
final AvailableStatus status = DiskYandexNet.parseInformationAPIAvailablecheckFiles(this, link, null, entries);
link.setAvailableStatus(status);
}
private void parseFilePropertiesWebsite(final DownloadLink dl, final Map<String, Object> entries) throws Exception {
final AvailableStatus status = DiskYandexNet.parseInformationWebsiteAvailablecheckFiles(this, dl, entries);
dl.setAvailableStatus(status);
private void parseFilePropertiesWebsite(final DownloadLink link, final Map<String, Object> entries) throws Exception {
final AvailableStatus status = DiskYandexNet.parseInformationWebsiteAvailablecheckFiles(this, link, entries);
link.setAvailableStatus(status);
}
}

View File

@ -197,18 +197,18 @@ public class FaceBookComGallery extends PluginForDecrypt {
}
/* Do some minor corrections of added link. */
/* Remove m.facebook.com as our crawler can't cope with those (old?) facebook website versions for mobile devices. */
String url = param.getCryptedUrl().replaceFirst("(?i)http://", "https://").replace("https://m.", "https://www.");
final String videoIDFRomEmbedURL = new Regex(url, "(?i)https?://[^/]+/video/embed\\?video_id=(\\d+)").getMatch(0);
String addedurl = param.getCryptedUrl().replaceFirst("(?i)http://", "https://").replace("https://m.", "https://www.");
final String videoIDFRomEmbedURL = new Regex(addedurl, "(?i)https?://[^/]+/video/embed\\?video_id=(\\d+)").getMatch(0);
// final String mobileSubdomain = new Regex(url, "(?i)https?:/(m\\.[^/]+)/.+").getMatch(0);
if (videoIDFRomEmbedURL != null) {
/* Small workaround for embedded videourls */
url = "https://www." + this.getHost() + "/watch/?v=" + videoIDFRomEmbedURL;
addedurl = "https://www." + this.getHost() + "/watch/?v=" + videoIDFRomEmbedURL;
}
// if (mobileSubdomain != null) {
// /* Remove mobile sobdomain */
// url = url.replaceFirst(Pattern.quote(mobileSubdomain), this.getHost());
// }
br.getPage(url);
br.getPage(addedurl);
if (br.getHttpConnection().getResponseCode() == 404) {
throw new PluginException(LinkStatus.ERROR_FILE_NOT_FOUND);
}
@ -291,11 +291,11 @@ public class FaceBookComGallery extends PluginForDecrypt {
}
if (FaceBookComVideos.isVideo(result)) {
videoPackages.put(contentID, contentIDPackages.get(contentID));
if (url.contains(contentID)) {
if (addedurl.contains(contentID) || br.getURL().contains(contentID)) {
contentIDOfSingleDesiredVideo = contentID;
}
} else if (FaceBookComVideos.isPhoto(result)) {
if (url.contains(contentID)) {
if (addedurl.contains(contentID) || br.getURL().contains(contentID)) {
contentIDOfSingleDesiredPhoto = contentID;
singleDesiredPhoto = result;
}
@ -362,6 +362,7 @@ public class FaceBookComGallery extends PluginForDecrypt {
}
ret.clear();
ret.addAll(resultsForOneDesiredVideo);
/* Do not return here since filenames will be set down below! */
}
if (singleDesiredPhoto != null) {
/* User wants single photo -> Try to find more metadata e.g. name of the uploader */
@ -393,30 +394,32 @@ public class FaceBookComGallery extends PluginForDecrypt {
for (final DownloadLink result : ret) {
FaceBookComVideos.setFilename(result);
final String videoID = result.getStringProperty(FaceBookComVideos.PROPERTY_CONTENT_ID);
if (videoID != null) {
final String description = result.getStringProperty(FaceBookComVideos.PROPERTY_DESCRIPTION);
FilePackage fp = packages.get(videoID);
if (fp == null) {
fp = FilePackage.getInstance();
final String title = result.getStringProperty(FaceBookComVideos.PROPERTY_TITLE);
final String uploaderNameForPackage = FaceBookComVideos.getUploaderNameAny(result);
if (uploaderNameForPackage != null && title != null) {
fp.setName(uploaderNameForPackage + " - " + title + " - " + videoID);
} else if (uploaderNameForPackage != null) {
fp.setName(uploaderNameForPackage + " - " + videoID);
} else if (title != null) {
fp.setName(title + " - " + videoID);
} else {
fp.setName(videoID);
}
if (!StringUtils.isEmpty(description)) {
fp.setComment(description);
}
packages.put(videoID, fp);
}
result._setFilePackage(fp);
result.setContainerUrl(br.getURL());
if (videoID == null) {
/* Other/external item */
continue;
}
final String description = result.getStringProperty(FaceBookComVideos.PROPERTY_DESCRIPTION);
FilePackage fp = packages.get(videoID);
if (fp == null) {
fp = FilePackage.getInstance();
final String title = result.getStringProperty(FaceBookComVideos.PROPERTY_TITLE);
final String uploaderNameForPackage = FaceBookComVideos.getUploaderNameAny(result);
if (uploaderNameForPackage != null && title != null) {
fp.setName(uploaderNameForPackage + " - " + title + " - " + videoID);
} else if (uploaderNameForPackage != null) {
fp.setName(uploaderNameForPackage + " - " + videoID);
} else if (title != null) {
fp.setName(title + " - " + videoID);
} else {
fp.setName(videoID);
}
if (!StringUtils.isEmpty(description)) {
fp.setComment(description);
}
packages.put(videoID, fp);
}
result._setFilePackage(fp);
result.setContainerUrl(br.getURL());
}
if (ret.isEmpty()) {
/*

File diff suppressed because it is too large Load Diff

View File

@ -84,7 +84,7 @@ public class YandexAlbum extends PluginForHost {
}
/* Find json object for current link ... */
final List<Object> modelObjects = (List<Object>) JavaScriptEngineFactory.jsonToJavaObject(jd.plugins.decrypter.DiskYandexNetFolder.regExJSON(this.br));
Map<String, Object> entries = jd.plugins.decrypter.DiskYandexNetFolder.findModel(modelObjects, "resources");
Map<String, Object> entries = findModel(modelObjects, "resources");
if (entries == null) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
}
@ -108,6 +108,23 @@ public class YandexAlbum extends PluginForHost {
}
}
public static Map<String, Object> findModel(final List<Object> modelObjects, final String targetModelName) {
Map<String, Object> entries = null;
boolean foundResourceModel = false;
for (final Object modelo : modelObjects) {
entries = (Map<String, Object>) modelo;
final String model = (String) entries.get("model");
if (targetModelName.equalsIgnoreCase(model)) {
foundResourceModel = true;
break;
}
}
if (!foundResourceModel) {
return null;
}
return entries;
}
/** Parses file info for photo/video (ALBUM) urls e.g. /album/blabla:5fffffce1939c0c46ffffffe */
public static AvailableStatus parseInformationAPIAvailablecheckAlbum(final Plugin plugin, final DownloadLink dl, final Map<String, Object> entries) throws Exception {
Map<String, Object> entriesMeta = (Map<String, Object>) entries.get("meta");