Skip to content

Commit f99a181

Browse files
sioaekoclaude
andcommitted
Show video/cloud links alongside script downloads in EroScripts
Detects links to MEGA, Google Drive, Pixeldrain, Dropbox, and other cloud services in EroScripts posts. Video links shown in blue with film icon, scripts shown with file icon. Video links open in browser, scripts download to script folder. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 823731f commit f99a181

File tree

1 file changed

+81
-11
lines changed

1 file changed

+81
-11
lines changed

src/components/EroScriptsPanel.tsx

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
Check,
1111
ChevronDown,
1212
ChevronUp,
13+
Film,
14+
FileText,
1315
} from 'lucide-react'
1416
import { useTranslation } from '../i18n'
1517

@@ -38,7 +40,7 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
3840
const [username, setUsername] = useState('')
3941
const [loggingIn, setLoggingIn] = useState(false)
4042
const [expandedTopic, setExpandedTopic] = useState<number | null>(null)
41-
const [downloadLinks, setDownloadLinks] = useState<Record<number, Array<{ filename: string; url: string }>>>({})
43+
const [downloadLinks, setDownloadLinks] = useState<Record<number, Array<{ filename: string; url: string; type: 'script' | 'video' }>>>({})
4244
const [downloading, setDownloading] = useState<string | null>(null)
4345
const [downloaded, setDownloaded] = useState<Set<string>>(new Set())
4446

@@ -120,7 +122,7 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
120122
const resp = await window.electronAPI.eroscriptsFetch(`${BASE_URL}/t/${topicId}.json`)
121123
if (resp.ok && resp.data) {
122124
const posts = resp.data.post_stream?.posts || []
123-
const links: Array<{ filename: string; url: string }> = []
125+
const links: Array<{ filename: string; url: string; type: 'script' | 'video' }> = []
124126

125127
for (const post of posts) {
126128
const cooked = post.cooked || ''
@@ -134,7 +136,7 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
134136
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url}`
135137
const filename = safeDecodeURI(linkText)
136138
if (!links.some(l => l.url === fullUrl)) {
137-
links.push({ filename, url: fullUrl })
139+
links.push({ filename, url: fullUrl, type: 'script' })
138140
}
139141
}
140142

@@ -145,7 +147,7 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
145147
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url}`
146148
if (!links.some(l => l.url === fullUrl)) {
147149
const filename = safeDecodeURI(fullUrl.split('/').pop() || 'script')
148-
links.push({ filename, url: fullUrl })
150+
links.push({ filename, url: fullUrl, type: 'script' })
149151
}
150152
}
151153

@@ -156,12 +158,36 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
156158
const fullUrl = link.url.startsWith('http') ? link.url : `${BASE_URL}${link.url}`
157159
if (!links.some(l => l.url === fullUrl)) {
158160
const rawName = link.title || link.url.split('/').pop() || 'script.funscript'
159-
links.push({ filename: safeDecodeURI(rawName), url: fullUrl })
161+
links.push({ filename: safeDecodeURI(rawName), url: fullUrl, type: 'script' })
162+
}
163+
}
164+
}
165+
}
166+
167+
// Detect video/cloud links from cooked HTML and link_counts
168+
const allHrefRegex = /<a[^>]+href="([^"]*)"[^>]*>/gi
169+
while ((match = allHrefRegex.exec(cooked)) !== null) {
170+
const url = match[1]
171+
const cloud = getCloudService(url)
172+
if (cloud && !links.some(l => l.url === url)) {
173+
links.push({ filename: cloud.label, url, type: 'video' })
174+
}
175+
}
176+
177+
if (post.link_counts) {
178+
for (const link of post.link_counts) {
179+
if (link.url) {
180+
const cloud = getCloudService(link.url)
181+
if (cloud && !links.some(l => l.url === link.url)) {
182+
links.push({ filename: cloud.label, url: link.url, type: 'video' })
160183
}
161184
}
162185
}
163186
}
164187
}
188+
189+
// Sort: scripts first, then video links
190+
links.sort((a, b) => (a.type === b.type ? 0 : a.type === 'script' ? -1 : 1))
165191
setDownloadLinks(prev => ({ ...prev, [topicId]: links }))
166192
}
167193
}
@@ -294,18 +320,29 @@ export default function EroScriptsPanel({ currentVideoName, scriptFolder }: EroS
294320
downloadLinks[result.topicId].map(link => (
295321
<button
296322
key={link.url}
297-
onClick={() => handleDownload(link.url, link.filename)}
298-
disabled={downloading === link.url}
299-
className="w-full flex items-center gap-2 px-2 py-1.5 bg-surface-300/50 hover:bg-surface-100/30 rounded text-[10px] transition-colors disabled:opacity-50"
323+
onClick={() => link.type === 'video' ? window.open(link.url, '_blank') : handleDownload(link.url, link.filename)}
324+
disabled={link.type === 'script' && downloading === link.url}
325+
className={`w-full flex items-center gap-2 px-2 py-1.5 rounded text-[10px] transition-colors disabled:opacity-50 ${
326+
link.type === 'video'
327+
? 'bg-blue-500/10 hover:bg-blue-500/20'
328+
: 'bg-surface-300/50 hover:bg-surface-100/30'
329+
}`}
300330
>
301-
{downloaded.has(link.url) ? (
331+
{link.type === 'video' ? (
332+
<Film size={11} className="text-blue-400 flex-shrink-0" />
333+
) : downloaded.has(link.url) ? (
302334
<Check size={11} className="text-green-400 flex-shrink-0" />
303335
) : downloading === link.url ? (
304336
<RefreshCw size={11} className="animate-spin text-accent flex-shrink-0" />
305337
) : (
306-
<Download size={11} className="text-accent flex-shrink-0" />
338+
<FileText size={11} className="text-accent flex-shrink-0" />
339+
)}
340+
<span className={`truncate ${link.type === 'video' ? 'text-blue-300' : 'text-text-secondary'}`}>
341+
{link.filename}
342+
</span>
343+
{link.type === 'video' && (
344+
<ExternalLink size={9} className="text-blue-400/50 flex-shrink-0 ml-auto" />
307345
)}
308-
<span className="text-text-secondary truncate">{link.filename}</span>
309346
</button>
310347
))
311348
) : (
@@ -352,6 +389,39 @@ function safeDecodeURI(str: string): string {
352389
try { return decodeURIComponent(str) } catch { return str }
353390
}
354391

392+
const CLOUD_SERVICES: Array<{ pattern: RegExp; label: string }> = [
393+
{ pattern: /mega\.nz|mega\.co\.nz/i, label: 'MEGA' },
394+
{ pattern: /drive\.google\.com/i, label: 'Google Drive' },
395+
{ pattern: /pixeldrain\.com/i, label: 'Pixeldrain' },
396+
{ pattern: /dropbox\.com/i, label: 'Dropbox' },
397+
{ pattern: /mediafire\.com/i, label: 'MediaFire' },
398+
{ pattern: /gofile\.(io|me)/i, label: 'GoFile' },
399+
{ pattern: /workupload\.com/i, label: 'WorkUpload' },
400+
{ pattern: /anonfiles\.com/i, label: 'AnonFiles' },
401+
{ pattern: /1fichier\.com/i, label: '1Fichier' },
402+
{ pattern: /sendspace\.com/i, label: 'SendSpace' },
403+
{ pattern: /rapidgator\.(net|asia)/i, label: 'Rapidgator' },
404+
{ pattern: /uploaded\.(net|to)/i, label: 'Uploaded' },
405+
{ pattern: /katfile\.com/i, label: 'Katfile' },
406+
{ pattern: /spankbang\.com/i, label: 'SpankBang' },
407+
{ pattern: /pornhub\.com/i, label: 'Pornhub' },
408+
{ pattern: /xhamster\.com/i, label: 'xHamster' },
409+
{ pattern: /xvideos\.com/i, label: 'XVideos' },
410+
{ pattern: /erome\.com/i, label: 'Erome' },
411+
{ pattern: /redgifs\.com/i, label: 'RedGIFs' },
412+
{ pattern: /onedrive\.live\.com|1drv\.ms/i, label: 'OneDrive' },
413+
]
414+
415+
function getCloudService(url: string): { label: string } | null {
416+
// Skip EroScripts internal links
417+
if (url.includes('eroscripts.com')) return null
418+
if (url.includes('/uploads/')) return null
419+
for (const svc of CLOUD_SERVICES) {
420+
if (svc.pattern.test(url)) return { label: svc.label }
421+
}
422+
return null
423+
}
424+
355425
function isFunscriptUrl(url: string): boolean {
356426
const lower = url.toLowerCase()
357427
return lower.includes('.funscript') || lower.includes('.zip') || lower.includes('.7z') || lower.includes('.rar') || lower.includes('/uploads/')

0 commit comments

Comments
 (0)