Auto-DL
// ==UserScript==
// @name LImgDLer
// @version 1.11
// @grant GM_download
// @grant GM_addStyle
// @include https://*.ladies.de/Sex-Anzeigen/*
// @include https://ladies.de/Sex-Anzeigen/*
// @include https://escorts24.de/*
// @connect video1.ladies.de
// @connect ladies.de
// @connect escorts24.de
// @namespace https://greasyfork.org/users/290665
// @description Auto-DL
// ==/UserScript==
/*jslint es6 */
"use strict";
let downloadFolder;
const SCRIPT_VERSION = (typeof GM_info !== 'undefined' && GM_info.script && GM_info.script.version)
? String(GM_info.script.version)
: 'unknown';
let phonews;
let NAMERAW, PHONE;
if (typeof jQuery === 'undefined') {
console.error('LImgDLer: jQuery not found. Aborting.');
} else {
jQuery(function () {
hideSpam();
setupMutationObserver();
if (jQuery('.kuenstlername').length) { // modern mode
NAMERAW = jQuery('.kuenstlername').first().text();
PHONE = jQuery('.contacts-data strong').first().text();
} else if (jQuery('.auftrag-name h3').length) { // themenladies
NAMERAW = jQuery('.auftrag-name h3').first().text();
PHONE = jQuery('p.telefon strong').first().text();
} else if (jQuery('div.full_pad.bigfont strong').length) { // classic
NAMERAW = jQuery('div.full_pad.bigfont strong').text();
PHONE = jQuery('.div_td_last.lalign.midfont.itxt_pad.icon_text').eq(1).text();
}
if (!PHONE || !NAMERAW) {
console.warn('LImgDLer: some selectors not found; proceeding with defaults.');
}
// Coerce and sanitize values, use safe defaults when missing
PHONE = PHONE ? String(PHONE).replace(/\s\/\s/g, '-') : '';
let NAME = NAMERAW ? String(NAMERAW).replace(/[^\w-\(\)äöüß+ ]/ig, '') : '';
downloadFolder = (NAME || PHONE) ? (NAME + ' - ' + PHONE) : 'unknown';
phonews = PHONE.replace(/[ \-]+/g, '');
// Build search term defensively (skip empty tokens)
const _terms = [];
if (NAME) _terms.push(`\"${NAME}\"`);
if (PHONE) _terms.push(`\"${PHONE}\"`);
if (phonews) _terms.push(phonews);
let SEARCHTERM = _terms.length ? `(${_terms.join(' OR ')})` : '';
let dlArea = jQuery('<div id="LImgDLer">'
+ '<div class="title"><span class="Llogo">LImgDLer<span class="Lversion">' + SCRIPT_VERSION + '</span></span> '
+ '<button id="dlbutton">📥 Download <b>' + downloadFolder + '</b></button>'
+ '<button id="lhbutton">🔍 search LH</button>'
+ '<button id="copybutton">📋 copy Text</button>'
+ '</div></div>');
jQuery('body').on('click', '#lhbutton', function (event) {
event.stopPropagation();
event.preventDefault();
let url = 'https://www.google.de/search?as_sitesearch=lusthaus.cc&q=';
url += encodeURIComponent(SEARCHTERM);
window.open(url);
});
jQuery(dlArea).find('button').data('name', downloadFolder);
jQuery('body').on('click', '#dlbutton', function (event) {
startDownloads(jQuery(event.currentTarget).data('name'));
});
jQuery('body').on('click', '#copybutton', function (event) {
try {
navigator.clipboard.writeText(adData()).catch(function (e) {
// older browsers might reject promises
throw e;
});
} catch (e) {
// fallback: open a prompt so user can copy manually
let text = adData();
try { window.prompt('Copy ad data (Ctrl+C, Enter):', text); } catch (ee) { alert('Copy failed, please copy manually:\n' + text); }
}
});
if (jQuery('.auftrag-title').length) { // modern mode
addStyle();
jQuery('.auftrag-title').first().append(dlArea);
} else if (jQuery('div#content div.container').length) { // themenladies
jQuery('div#content div.container').first().append(dlArea);
addStyle("themen");
} else if (jQuery('div.sitemenu').length) { // classic
jQuery('div.sitemenu').append(dlArea);
addStyle("classic");
}
jQuery('#lhbutton').attr('title', SEARCHTERM);
});
}
function startDownloads(folderName) {
let downloadlist = [];
if (jQuery('.rsNav img').length) {
jQuery('.rsNav img').each(function () {
let link = jQuery(this).attr('src');
if (link.match(/-FK\./)) {
return; // skip videos
}
link = link.replace(/\?.*/, '');
downloadlist.push(link.replace(/(\d+)-F(\d+)\./, '$1-A$2.'));
});
jQuery('a.gallery_video').each(function () { // get videos
downloadlist.push(jQuery(this).attr('href'));
});
dlAll(folderName, downloadlist);
} else {
let images = jQuery('.photo-slide img');
images.each(function () {
let imgURL = jQuery(this).attr('src');
downloadlist.push(imgURL);
});
let videos = jQuery('.video-slide lds-video-player');
videos.each(function () {
let videoOptions = jQuery(this).attr('options');
if (videoOptions) {
try {
let options = JSON.parse(videoOptions);
if (options && options.sources && options.sources.length) {
downloadlist.push(...options.sources);
}
} catch (e) {
console.warn('Failed to parse video options:', videoOptions);
}
}
});
dlAll(folderName, downloadlist);
}
}
function dlAll(path, downloadlist) {
jQuery('#LImgDLerdialog').remove();
const dialog = jQuery('<div id="LImgDLerdialog" title="Download to ' + path + ' ..."></div>').appendTo(jQuery('#LImgDLer'));
for (let URL of downloadlist) {
let originalName = (URL || '').replace(/.*\//, '').replace(/\?.*/g, '');
let filename = originalName || 'file';
let safeFolder = (path || '').replace(/[^\w\-\(\)\ ]/g, '_');
// Try folder-style name first (safeFolder/filename) to preserve original behavior.
// If the userscript manager doesn't support folders, retry with a flat fallback.
let fileWithSlash = safeFolder ? (safeFolder + '/' + filename) : filename;
let fallbackFile = safeFolder ? (safeFolder + ' - ' + filename) : filename;
URL = (URL || '').replace(/^\/\//, 'https://');
let line = jQuery('<div class="RLDL" data-name="' + filename + '">' + filename + '</div>\n').appendTo(dialog);
(function (url, initialPath, fallbackPath, filenameLocal, linediv) {
const attemptDownload = function (filepath, triedFallback) {
GM_download({
url: url,
name: filepath,
saveAs: false,
onerror: function (err) {
let errText = 'Unknown error';
try {
if (!err) errText = 'no error object';
else if (err.error) errText = err.error;
else if (err.message) errText = err.message;
else errText = JSON.stringify(err);
} catch (ee) { errText = String(err); }
// If we tried the folder path and it failed, retry with fallback once.
if (!triedFallback && filepath === initialPath && fallbackPath !== initialPath) {
console.warn('LImgDLer: folder-style download failed, retrying flat filename.', err);
attemptDownload(fallbackPath, true);
return;
}
jQuery(linediv).append('<span class="download_error">ERROR: ' + errText + '</span>');
console.error('LImgDLer download error:', err);
},
onload: function () {
jQuery(linediv).append('<span class="download_ok">✓</span>');
}
});
};
// Start with folder-style path, fallback will be attempted automatically on error.
attemptDownload(fileWithSlash, false);
})(URL, fileWithSlash, fallbackFile, filename, line);
}
}
function adData() {
// Defensive phone formatting (unique variants)
const raw = (phonews || '').trim();
const local = raw.replace(/\+49\(0\)/, '0');
let dash = local.replace(/^(01[67].)/, '$1-');
dash = dash.replace(/^(015510)/, '$1-');
dash = dash.replace(/^(015..)(\d+)$/, '$1-$2');
const intl = raw.replace(/^0([1-9])/, '+49$1');
const phones = Array.from(new Set([dash, local, intl].filter(Boolean)));
// Attributes
const attrs = [];
jQuery('.attribute-column > .row').each(function () {
const key = jQuery(this).find('strong').first().text().trim();
const vals = [];
jQuery(this).find('p').each(function () { vals.push(jQuery(this).text().trim()); });
const v = vals.join(', ');
if (key && v) attrs.push(`${key}: [B]${v}[/B]`);
});
// Club and address
const clubname = jQuery('[property=locationName]').first().text().trim();
const clublink = jQuery('a[property="ladyLocation"]').attr('href') || '';
const club = clubname ? `[URL=https://ladies.de${clublink}]${clubname}[/URL]` : '';
const street = jQuery('[itemprop=address] [property=streetAddress]').first().text() || '';
const postal = jQuery('[itemprop=address] [itemprop=postalCode]').first().attr('content') || '';
const city = jQuery('[itemprop=address] [itemprop=addressLocality]').first().text() || jQuery('.contacts-data .address-details').first().text().replace(/[\s\n]+/g, ' ').trim() || '';
const addressText = [street, postal, city].map(s => (s||'').trim()).filter(Boolean).join(' ');
const addressBB = addressText ? `[URL=https://www.google.de/maps/place/${encodeURIComponent(addressText)}]${addressText}[/URL]` : '';
// Description: concatenate multiple blocks if present, keep block separation
let description = '';
const descBlocks = [];
const descContainer = jQuery('.einzelansicht-text div');
if (descContainer.length) {
// prefer direct child blocks (div, p, section)
descContainer.children('div, p, section').each(function () {
let html = jQuery(this).clone().find('span').remove().end().html() || '';
// preserve <br> as newlines
html = html.replace(/<br\s*\/?>/gi, '\n');
// strip remaining tags
let txt = html.replace(/<[^>]+>/g, '');
// decode HTML entities
try { txt = jQuery('<textarea/>').html(txt).text(); } catch (er) {}
txt = txt.replace(/\n\s*\n+/g, '\n\n').trim();
if (txt) descBlocks.push(txt);
});
// fallback: if no child blocks, use html content and split paragraphs
if (!descBlocks.length) {
const fullHtml = descContainer.html() || '';
if (fullHtml) {
let tmp = fullHtml.replace(/<br\s*\/?>/gi, '\n');
tmp = tmp.replace(/<[^>]+>/g, '');
try { tmp = jQuery('<textarea/>').html(tmp).text(); } catch (er) {}
const parts = tmp.split(/\n\s*\n+/).map(s => s.trim()).filter(Boolean);
if (parts.length) descBlocks.push(...parts);
}
}
description = descBlocks.join('\n\n');
}
// Services
const services = [];
jQuery('.profil .column-group').each(function () {
const key = jQuery(this).find('strong').first().text().trim();
const vals = [];
jQuery(this).find('.attribute-item').each(function () { vals.push(jQuery(this).text().trim()); });
const v = vals.join(', ');
if (key && v) services.push(`${key}: [B]${v}[/B]`);
});
const title = NAMERAW || downloadFolder || '';
const pageURL = window.location.href || '';
// Build BBCode with German headings, single-line attributes and size wrappers
const parts = [];
parts.push('Infoservice');
parts.push('[QUOTE]');
if (title) parts.push(`[URL=${pageURL}][B]${title}[/B][/URL]`);
// size wrapper for compact content
parts.push('[SIZE="1"]');
if (description) {
parts.push(description);
parts.push('\n'); // ensure separation from next section
}
if (attrs.length) {
parts.push(attrs.join('\n'));
}
if (services.length) {
parts.push('\n[B]Service[/B]');
parts.push(services.join('\n'));
}
if (phones.length) {
parts.push('\n[B]Telefon[/B]');
// large primary phone, others inline
parts.push(`[B][SIZE=3]${phones[0]}[/SIZE][/B]` + (phones.length > 1 ? (', ' + phones.slice(1).join(' / ')) : ''));
}
if (club || addressBB) {
parts.push('\n[B]Adresse[/B]');
parts.push(((club ? club + ', ' : '') + (addressBB || '')).trim());
}
parts.push('[/SIZE]');
parts.push('[/QUOTE]');
const dataHTML = parts.join('\n').replace(/ {2,}/g, ' ').replace(/\n{3,}/g, '\n\n');
return dataHTML;
}
function hideSpam() {
jQuery('div.anzeige').has('span.webcam-markierung').remove();
jQuery('div.anzeige').has('div.closed-info').css('opacity', '0.3');
jQuery('section#page-top-sol').remove();
}
function setupMutationObserver() {
const observer = new MutationObserver(hideSpam);
let target = jQuery('ul.pagination');
if (target.length) {
observer.observe(jQuery(target).get(0), {
attributes: true,
childList: true,
subtree: true
});
}
}
function addStyle(mode) {
let styles = [];
styles["classic"] = `
#LImgDLer {
left: 174px;
top: 109px;
right: 202px;
line-height: 10px;
font-size:9px;
}
.Llogo {
font-size: 16px;
top: 2px;
}
#LImgDLer button {
padding: 4px 12px;
font-size:11px;
}
button:hover {
cursor:pointer;
}
`;
styles["themen"] = `
div#content div.container {
position:relative;
}
#LImgDLer {
left: 16px;
top: 50px;
right: 15px;
line-height: 10px;
}
.Llogo {
font-size: 17px;
top: 1px;
}
#LImgDLer button {
padding: 4px 12px;
}
`;
GM_addStyle(`
.download_error {
color: white;
background-color: #880010;
padding: 1px 4px;
border-radius: 2px;
margin: 0px 4px;
}
.download_ok {
color: white;
background-color: #10a020;
padding: 1px 4px;
border-radius: 2px;
margin: 0px 4px;
}
#LImgDLer {
max-height: 180px;
font-size: 10px;
position: absolute;
left: 240px;
top: 0;
right: 30px;
}
#LImgDLer button {
background-color: #e28c13;
border: none;
margin: 0px 4px;
padding: 10px 12px;
line-height: 15px;
font-size: 15px;
color: white;
}
#LImgDLer > div.title {
background-color:#6f789f;
}
#LImgDLerdialog {
background-color: rgba(255, 255, 255, .52);
overflow: auto;
column-width: 110px;
padding:4px;
}
.Llogo {
padding: 0 12px;
color: #e69536;
font-size: 24px;
font-weight: 700;
text-shadow: 0 0 1.5px black;
font-style: italic;
top: 3px;
position: relative;
line-height: 12px;
}
.Lversion {
position: absolute;
top: -6px;
right: -6px;
background: #9ba4cc;
color: white;
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
font-weight: 700;
line-height: 1;
box-shadow: 1px 2px 6px #00000050;
}
`);
if (mode && styles[mode]) {
GM_addStyle(styles[mode]);
}
}