|
16 | 16 | package jd.plugins.decrypter; |
17 | 17 |
|
18 | 18 | import java.util.ArrayList; |
| 19 | +import java.util.Arrays; |
19 | 20 | import java.util.HashSet; |
20 | 21 | import java.util.List; |
21 | 22 | import java.util.Map; |
|
45 | 46 | import jd.plugins.PluginForDecrypt; |
46 | 47 | import jd.plugins.hoster.FileFactory; |
47 | 48 |
|
48 | | -@DecrypterPlugin(revision = "$Revision: 51653 $", interfaceVersion = 2, names = {}, urls = {}) |
| 49 | +@DecrypterPlugin(revision = "$Revision: 51663 $", interfaceVersion = 2, names = {}, urls = {}) |
49 | 50 | @PluginDependencies(dependencies = { FileFactory.class }) |
50 | 51 | public class FilefactoryComFolder extends PluginForDecrypt { |
51 | 52 | public FilefactoryComFolder(PluginWrapper wrapper) { |
@@ -121,18 +122,73 @@ public ArrayList<DownloadLink> decryptIt(final CryptedLink param, ProgressContro |
121 | 122 | /* Empty folder */ |
122 | 123 | throw new DecrypterRetryException(RetryReason.EMPTY_FOLDER); |
123 | 124 | } |
124 | | - String folder_url_without_params = URLHelper.getUrlWithoutParams(br.getURL()); |
125 | | - if (!folder_url_without_params.endsWith("/")) { |
126 | | - folder_url_without_params += "/"; |
127 | | - } |
128 | | - final String folderTitle = br.getRegex("<h1>Files in\\s*<span>([^<]+)</span>").getMatch(0); |
| 125 | + /* Folder title is not available in CSV file thus to get it, we need to look into the html code of the older URL. */ |
| 126 | + String folderTitle = br.getRegex("<h1>Files in\\s*<span>([^<]+)</span>").getMatch(0); |
129 | 127 | final FilePackage fp = FilePackage.getInstance(); |
130 | 128 | if (!StringUtils.isEmpty(folderTitle)) { |
131 | | - fp.setName(Encoding.htmlDecode(folderTitle).trim()); |
| 129 | + folderTitle = Encoding.htmlDecode(folderTitle).trim(); |
| 130 | + fp.setName(folderTitle); |
132 | 131 | } else { |
133 | 132 | logger.warning("Failed to find folder title"); |
134 | 133 | fp.setName(folder_id); |
135 | 134 | } |
| 135 | + fp.setPackageKey(this.getHost() + "://folder/" + folder_id); |
| 136 | + parse_folder_csv_export: { |
| 137 | + /* Try to extract folder information from CSV since this is the better source of data. */ |
| 138 | + final String folderExportLink = br.getRegex("(/folder/" + folder_id + "/\\?export=1)").getMatch(0); |
| 139 | + if (folderExportLink == null) { |
| 140 | + logger.warning("CSV crawler: Cannot parse folder CSV export because: Failed to find folder export link"); |
| 141 | + break parse_folder_csv_export; |
| 142 | + } |
| 143 | + try { |
| 144 | + final Browser brc = br.cloneBrowser(); |
| 145 | + /* Allow unlimited size of CSV files. */ |
| 146 | + brc.setLoadLimit(Integer.MAX_VALUE); |
| 147 | + brc.getPage(folderExportLink); |
| 148 | + final String exportName = getFileNameFromConnection(brc.getHttpConnection()); |
| 149 | + if (!StringUtils.endsWithCaseInsensitive(exportName, ".csv") || brc.getHttpConnection().getResponseCode() != 200) { |
| 150 | + logger.warning("CSV crawler: csv export failed"); |
| 151 | + break parse_folder_csv_export; |
| 152 | + } |
| 153 | + final String csvContent = brc.getRequest().getHtmlCode(); |
| 154 | + // Split by line breaks |
| 155 | + final String[] lines = csvContent.split("\\r?\\n"); |
| 156 | + /* Validate CSV integrity by checking for the expected CSV headers. */ |
| 157 | + final List<String> header = Arrays.asList(parseCsvLine(lines[0])); |
| 158 | + final int filenameIndex = header.indexOf("filename"); |
| 159 | + final int sizeIndex = header.indexOf("size"); |
| 160 | + final int urlIndex = header.indexOf("url"); |
| 161 | + if (filenameIndex == -1 || sizeIndex == -1 || urlIndex == -1) { |
| 162 | + logger.warning("CSV crawler: cannot find all required columns in csv"); |
| 163 | + break parse_folder_csv_export; |
| 164 | + } |
| 165 | + for (int i = 1; i < lines.length; i++) { // Start at 1 to skip CSV header |
| 166 | + final String line = lines[i]; |
| 167 | + final String[] data = parseCsvLine(line); |
| 168 | + final String filename = data[filenameIndex]; |
| 169 | + final String sizeBytesStr = data[sizeIndex]; |
| 170 | + // final String createdDate = data[2]; |
| 171 | + final String url = data[urlIndex]; |
| 172 | + final DownloadLink link = this.createDownloadlink(url); |
| 173 | + link.setFinalFileName(filename); |
| 174 | + link.setVerifiedFileSize(Long.parseLong(sizeBytesStr)); |
| 175 | + link._setFilePackage(fp); |
| 176 | + link.setAvailable(true); |
| 177 | + ret.add(link); |
| 178 | + } |
| 179 | + logger.info("CSV crawler: Success!"); |
| 180 | + return ret; |
| 181 | + } catch (Exception e) { |
| 182 | + logger.log(e); |
| 183 | + logger.info("CSV crawler: Failed!"); |
| 184 | + break parse_folder_csv_export; |
| 185 | + } |
| 186 | + } |
| 187 | + /* Base URL needed for pagination. */ |
| 188 | + String folder_url_without_params = URLHelper.getUrlWithoutParams(br.getURL()); |
| 189 | + if (!folder_url_without_params.endsWith("/")) { |
| 190 | + folder_url_without_params += "/"; |
| 191 | + } |
136 | 192 | int pageMax = 1; |
137 | 193 | final HashSet<String> dupes = new HashSet<String>(); |
138 | 194 | pagination: for (int page = 1; page <= pageMax; page++) { |
@@ -298,6 +354,44 @@ public ArrayList<DownloadLink> decryptIt(final CryptedLink param, ProgressContro |
298 | 354 | return ret; |
299 | 355 | } |
300 | 356 |
|
| 357 | + /** |
| 358 | + * Parses a CSV line correctly - handles quotes, commas, and escaped quotes within quoted values Java 1.6 compatible version |
| 359 | + * |
| 360 | + * CSV escape rules: - Fields with commas or quotes are wrapped in quotes: "value" - Quotes inside quoted fields are escaped by doubling |
| 361 | + * them: "" |
| 362 | + * |
| 363 | + * Example: "File with ""quotes"" in name.zip" represents: File with "quotes" in name.zip |
| 364 | + */ |
| 365 | + private static String[] parseCsvLine(String line) { |
| 366 | + List<String> result = new ArrayList<String>(); |
| 367 | + StringBuilder current = new StringBuilder(); |
| 368 | + boolean inQuotes = false; |
| 369 | + for (int i = 0; i < line.length(); i++) { |
| 370 | + char c = line.charAt(i); |
| 371 | + if (c == '"') { |
| 372 | + // Check if this is an escaped quote ("") |
| 373 | + if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == '"') { |
| 374 | + // Escaped quote: add one quote to output and skip the next one |
| 375 | + current.append('"'); |
| 376 | + i++; // Skip next quote |
| 377 | + } else { |
| 378 | + // Toggle quote mode (start or end of quoted field) |
| 379 | + inQuotes = !inQuotes; |
| 380 | + } |
| 381 | + } else if (c == ',' && !inQuotes) { |
| 382 | + // Comma outside quotes = new field |
| 383 | + result.add(current.toString()); |
| 384 | + current = new StringBuilder(); |
| 385 | + } else { |
| 386 | + // Regular character |
| 387 | + current.append(c); |
| 388 | + } |
| 389 | + } |
| 390 | + // Add last field |
| 391 | + result.add(current.toString()); |
| 392 | + return result.toArray(new String[result.size()]); |
| 393 | + } |
| 394 | + |
301 | 395 | private String generateSingleFileLink(final String file_id, final String filename) { |
302 | 396 | String url = "https://www." + this.getHost() + "/file/" + file_id; |
303 | 397 | if (filename != null) { |
|
0 commit comments