| <!DOCTYPE html> |
| <html lang="ur" dir="rtl"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| <title>📖 Urdu Book Viewer</title> |
| <link href="https://fonts.googleapis.com/css2?family=Noto+Nastaliq+Urdu:wght@400;700&family=Amiri:wght@400;700&display=swap" rel="stylesheet"> |
| <style> |
| body {font-family: 'Noto Nastaliq Urdu', 'Amiri', serif; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 1rem; color: #333;} |
| .container {max-width: 900px; margin: 0 auto;} |
| .header {text-align: center; color: white; margin-bottom: 2rem;} |
| .header h1 {font-size: 2.5rem; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);} |
| .header p {font-size: 1.1rem; opacity: 0.9; margin: 0.5rem 0;} |
| .page {background: rgba(255, 255, 255, 0.95); padding: 3rem; border-radius: 20px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); margin-bottom: 2rem; position: relative; line-height: 2; font-size: 1.1rem; transition: transform 0.3s ease;} |
| .page:hover {transform: translateY(-4px);} |
| .page-number {position: absolute; bottom: 1rem; left: 1rem; font-size: 0.9rem; color: #666; background: rgba(255, 255, 255, 0.8); padding: 0.25rem 0.75rem; border-radius: 20px;} |
| .stats {display: flex; justify-content: space-around; background: rgba(255, 255, 255, 0.1); padding: 1rem; border-radius: 12px; margin-bottom: 1rem; color: white;} |
| .stat-item {text-align: center;} |
| .stat-number {font-size: 1.5rem; font-weight: bold; display: block;} |
| iframe { max-width: 100%; border: none; border-radius: 12px; margin: 1rem 0; } |
| |
| |
| .half-title-page { |
| text-align: center; |
| min-height: 60vh; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| background: linear-gradient(120deg, #f8fafc 0%, #e9d8fd 100%); |
| animation: fadeIn 1.2s; |
| position: relative; |
| flex-direction: column; |
| } |
| .half-title-page h2 { |
| font-size: 3.5rem; |
| color: #764ba2; |
| margin: 0; |
| padding: 2rem 1rem 1rem 1rem; |
| font-family: 'Noto Nastaliq Urdu', serif; |
| text-shadow: 0 4px 24px #b794f4, 0 1px 0 #fff; |
| letter-spacing: 2px; |
| position: relative; |
| } |
| .half-title-page .divider { |
| width: 80px; |
| height: 4px; |
| background: linear-gradient(90deg, #764ba2 0%, #667eea 100%); |
| border-radius: 2px; |
| margin: 1.5rem auto 0 auto; |
| } |
| .half-title-page .motif { |
| font-size: 2.2rem; |
| color: #b794f4; |
| margin-top: 1.2rem; |
| } |
| |
| .title-page { |
| text-align: center; |
| min-height: 80vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| background: linear-gradient(120deg, #fff 0%, #e9d8fd 100%); |
| padding: 2rem; |
| border: 2px solid #d6bcfa; |
| border-radius: 32px; |
| box-shadow: 0 8px 32px rgba(118, 75, 162, 0.13); |
| animation: fadeInUp 1.2s; |
| position: relative; |
| } |
| .title-page h1 { |
| font-size: 3.2rem; |
| color: #764ba2; |
| margin-bottom: 2rem; |
| font-family: 'Noto Nastaliq Urdu', serif; |
| text-shadow: 0 2px 12px #e9d8fd; |
| letter-spacing: 1.5px; |
| animation: fadeIn 1.2s; |
| } |
| .title-page .author { |
| font-size: 1.7rem; |
| color: #4b3869; |
| margin-bottom: 1.2rem; |
| font-family: 'Noto Nastaliq Urdu', serif; |
| animation: fadeIn 1.6s; |
| } |
| .title-page .publisher { |
| font-size: 1.2rem; |
| color: #6b46c1; |
| margin-top: 2.5rem; |
| font-family: 'Amiri', serif; |
| letter-spacing: 1px; |
| animation: fadeIn 2s; |
| } |
| .title-page .motif { |
| font-size: 2.2rem; |
| color: #b794f4; |
| margin-bottom: 1.5rem; |
| } |
| |
| .dedication-page { |
| text-align: center; |
| min-height: 60vh; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| background: linear-gradient(120deg, #f8fafc 0%, #e9d8fd 100%); |
| font-style: italic; |
| animation: fadeInUp 1.2s; |
| position: relative; |
| } |
| .dedication-page .dedication-box { |
| background: rgba(255,255,255,0.95); |
| border: 2px solid #d6bcfa; |
| border-radius: 24px; |
| box-shadow: 0 4px 24px #b794f4; |
| padding: 2.5rem 2rem 2rem 2rem; |
| max-width: 600px; |
| margin: auto; |
| position: relative; |
| font-family: 'Noto Nastaliq Urdu', serif; |
| } |
| .dedication-page .dedication-text { |
| font-size: 1.6rem; |
| color: #4b3869; |
| line-height: 2.1; |
| margin-top: 1.2rem; |
| } |
| .dedication-page .quote-icon { |
| font-size: 2.5rem; |
| color: #b794f4; |
| position: absolute; |
| top: 1.2rem; |
| left: 1.2rem; |
| opacity: 0.7; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| @keyframes fadeInUp { |
| from { opacity: 0; transform: translateY(40px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| .dedication-page .dedication-urdu { |
| font-size: 1.7rem; |
| color: #4b3869; |
| font-family: 'Noto Nastaliq Urdu', serif; |
| direction: rtl; |
| text-align: right; |
| margin-bottom: 1.2rem; |
| line-height: 2.1; |
| } |
| .dedication-page .dedication-english { |
| font-size: 1.3rem; |
| color: #764ba2; |
| font-family: 'Amiri', serif; |
| font-style: italic; |
| direction: ltr; |
| text-align: left; |
| margin-top: 1.2rem; |
| line-height: 1.8; |
| } |
| .dedication-page .dedication-divider { |
| width: 60px; |
| height: 2px; |
| background: linear-gradient(90deg, #b794f4 0%, #764ba2 100%); |
| margin: 1.2rem auto 1.2rem auto; |
| border-radius: 1px; |
| } |
| .download-btn { |
| background: linear-gradient(90deg, #764ba2 0%, #667eea 100%); |
| color: #fff; |
| font-size: 1.1rem; |
| font-family: inherit; |
| border: none; |
| border-radius: 24px; |
| padding: 0.7rem 2.2rem; |
| margin-bottom: 1.2rem; |
| cursor: pointer; |
| box-shadow: 0 2px 8px rgba(118,75,162,0.13); |
| transition: background 0.3s, transform 0.2s; |
| } |
| .download-btn:hover { |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); |
| transform: translateY(-2px) scale(1.04); |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="header"> |
| <h1>📖 اردو کتاب ریڈر</h1> |
| <div style="text-align:center; margin-top:30px; color:white; font-size: 0.9rem;"> |
| Developed by <strong>Abdullah Tarar</strong> |
| </div> |
| </div> |
|
|
| <div id="downloadContainer" style="text-align:center; margin-bottom: 1.5rem;"></div> |
| <div id="statsContainer"></div> |
| <div id="bookContainer"></div> |
| </div> |
|
|
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script> |
| <script> |
| let videoMap = {}; |
| |
| async function loadStory() { |
| try { |
| const params = new URLSearchParams(window.location.search); |
| const fileName = params.get('file') || 'Story.txt'; |
| |
| const videoResponse = await fetch('Url.json'); |
| const rawMap = await videoResponse.json(); |
| videoMap = processVideoMap(rawMap); |
| |
| const response = await fetch(fileName); |
| const content = await response.text(); |
| renderBook(content); |
| } catch (error) { |
| console.error('کہانی لوڈ کرنے میں مسئلہ ہوا:', error); |
| } |
| } |
| |
| function processVideoMap(rawMap) { |
| const map = {}; |
| for (let key in rawMap) { |
| const url = rawMap[key].trim(); |
| |
| if (url.startsWith("<iframe")) { |
| map[key] = url; |
| } else if (url.includes("watch?v=")) { |
| const id = url.split("watch?v=")[1].split("&")[0]; |
| map[key] = `<iframe width="100%" height="315" src="https://www.youtube.com/embed/${id}" allowfullscreen></iframe>`; |
| } else if (url.includes("youtu.be/")) { |
| const id = url.split("youtu.be/")[1].split("?")[0]; |
| map[key] = `<iframe width="100%" height="315" src="https://www.youtube.com/embed/${id}" allowfullscreen></iframe>`; |
| } else { |
| map[key] = `<iframe width="100%" height="315" src="${url}" allowfullscreen></iframe>`; |
| } |
| } |
| return map; |
| } |
| |
| function renderBook(text) { |
| const container = document.getElementById("bookContainer"); |
| const statsContainer = document.getElementById("statsContainer"); |
| container.innerHTML = ""; |
| |
| const wordCount = text.trim().split(/\s+/).length; |
| const charCount = text.length; |
| const pageSize = 1600; |
| let chunks = splitText(text, pageSize); |
| |
| |
| chunks = processSpecialPages(chunks); |
| |
| statsContainer.innerHTML = ` |
| <div class="stats"> |
| <div class="stat-item"><span class="stat-number">${chunks.length}</span> صفحات</div> |
| <div class="stat-item"><span class="stat-number">${wordCount}</span> الفاظ</div> |
| <div class="stat-item"><span class="stat-number">${charCount}</span> حروف</div> |
| </div>`; |
| |
| chunks.forEach((chunk, index) => { |
| const page = document.createElement("div"); |
| page.className = "page"; |
| |
| if (chunk.type === 'half-title') { |
| page.innerHTML = ` |
| <div class="half-title-page"> |
| <h2>${chunk.content}</h2> |
| <div class="divider"></div> |
| <div class="motif">★</div> |
| </div>`; |
| } else if (chunk.type === 'title') { |
| const [title, author, publisher] = chunk.content.split('\n').map(line => line.trim()); |
| page.innerHTML = ` |
| <div class="title-page"> |
| <div class="motif">❦</div> |
| <h1>${title}</h1> |
| <div class="author">تصنیف: ${author}</div> |
| <div class="publisher">${publisher}</div> |
| </div>`; |
| } else if (chunk.type === 'dedication') { |
| |
| const lines = chunk.content.split(/\r?\n/).map(l => l.trim()).filter(Boolean); |
| const urduLines = lines.filter(l => /[\u0600-\u06FF]/.test(l)); |
| const englishLines = lines.filter(l => !(/[\u0600-\u06FF]/.test(l)) && l); |
| let dedicationHtml = '<div class="dedication-page">\n <div class="dedication-box">\n <span class="quote-icon">❝</span>'; |
| if (urduLines.length && englishLines.length) { |
| dedicationHtml += `<div class="dedication-urdu">${urduLines.map(l => `<div>${l}</div>`).join('')}</div>`; |
| dedicationHtml += '<div class="dedication-divider"></div>'; |
| dedicationHtml += `<div class="dedication-english">${englishLines.map(l => `<div>${l}</div>`).join('')}</div>`; |
| } else if (urduLines.length) { |
| dedicationHtml += `<div class="dedication-urdu">${urduLines.map(l => `<div>${l}</div>`).join('')}</div>`; |
| } else { |
| dedicationHtml += `<div class="dedication-english">${englishLines.map(l => `<div>${l}</div>`).join('')}</div>`; |
| } |
| dedicationHtml += ' </div>\n</div>'; |
| page.innerHTML = dedicationHtml; |
| } else { |
| page.innerHTML = parseContent(chunk); |
| } |
| |
| const pageNumber = document.createElement("div"); |
| pageNumber.className = "page-number"; |
| pageNumber.textContent = `صفحہ ${index + 1}`; |
| page.appendChild(pageNumber); |
| container.appendChild(page); |
| }); |
| } |
| |
| function processSpecialPages(chunks) { |
| const processedChunks = []; |
| for (let chunk of chunks) { |
| if (typeof chunk === 'string') { |
| let text = chunk; |
| let found = false; |
| |
| while (true) { |
| let minIdx = text.length; |
| let type = null; |
| let trigger = null; |
| if (text.includes('...Half') && text.indexOf('...Half') < minIdx) { |
| minIdx = text.indexOf('...Half'); |
| type = 'half-title'; |
| trigger = '...Half'; |
| } |
| if (text.includes('...Title') && text.indexOf('...Title') < minIdx) { |
| minIdx = text.indexOf('...Title'); |
| type = 'title'; |
| trigger = '...Title'; |
| } |
| if (text.includes('...Page') && text.indexOf('...Page') < minIdx) { |
| minIdx = text.indexOf('...Page'); |
| type = 'dedication'; |
| trigger = '...Page'; |
| } |
| if (type) { |
| |
| const before = text.slice(0, minIdx).trim(); |
| if (before) processedChunks.push(before); |
| |
| const content = text.slice(0, minIdx).trim(); |
| if (content || type !== 'dedication') processedChunks.push({ type, content }); |
| const afterTrigger = text.slice(minIdx + trigger.length).trim(); |
| text = afterTrigger; |
| found = true; |
| if (!text) break; |
| } else { |
| |
| if (text.trim()) processedChunks.push(text.trim()); |
| break; |
| } |
| } |
| if (!found && text.trim()) processedChunks.push(text.trim()); |
| } else { |
| processedChunks.push(chunk); |
| } |
| } |
| |
| return processedChunks.filter( |
| c => (typeof c === 'string' && c.trim()) || (typeof c === 'object' && (c.content && c.content.trim() || c.type !== 'dedication')) |
| ); |
| } |
| |
| function parseContent(text) { |
| if (typeof text === 'object') return text.content; |
| |
| let html = text.replace(/\n\n/g, '</p><p>').replace(/\n/g, '<br>'); |
| html = html.replace(/\[(.*?)\]/g, (match, key) => { |
| return videoMap[key] || match; |
| }); |
| return `<p>${html}</p>`; |
| } |
| |
| function splitText(text, size) { |
| const parts = []; |
| const paragraphs = text.split('\n\n'); |
| let currentChunk = ''; |
| for (const paragraph of paragraphs) { |
| if (currentChunk.length + paragraph.length > size && currentChunk.length > 0) { |
| parts.push(currentChunk.trim()); |
| currentChunk = paragraph; |
| } else { |
| currentChunk += (currentChunk ? '\n\n' : '') + paragraph; |
| } |
| } |
| if (currentChunk.trim()) { |
| parts.push(currentChunk.trim()); |
| } |
| return parts.length > 0 ? parts : [text]; |
| } |
| |
| window.addEventListener('load', () => { |
| loadStory().then(() => { |
| showDownloadButton(); |
| }); |
| }); |
| |
| function showDownloadButton() { |
| const downloadDiv = document.getElementById('downloadContainer'); |
| downloadDiv.innerHTML = `<button id="downloadPdfBtn" class="download-btn">⬇️ کتاب PDF میں ڈاؤن لوڈ کریں</button>`; |
| const btn = document.getElementById('downloadPdfBtn'); |
| |
| btn.onclick = async function() { |
| try { |
| btn.disabled = true; |
| btn.textContent = 'Generating PDF...'; |
| |
| |
| const params = new URLSearchParams(window.location.search); |
| const fileName = params.get('file') || 'Story.txt'; |
| |
| |
| const response = await fetch(fileName); |
| const content = await response.text(); |
| |
| |
| const pdfContainer = document.createElement('div'); |
| pdfContainer.style.display = 'none'; |
| document.body.appendChild(pdfContainer); |
| |
| |
| const pageSize = 1600; |
| let chunks = splitText(content, pageSize); |
| chunks = processSpecialPages(chunks); |
| |
| |
| pdfContainer.innerHTML = ` |
| <div id="pdfContent" style="font-family: 'Noto Nastaliq Urdu', 'Amiri', serif; direction: rtl;"> |
| ${chunks.map((chunk, index) => { |
| let pageHtml = ''; |
| if (typeof chunk === 'object') { |
| // Handle special pages |
| if (chunk.type === 'half-title') { |
| pageHtml = ` |
| <div style="text-align: center; padding: 40px; margin: 20px;"> |
| <h2 style="font-size: 32px; color: #764ba2;">${chunk.content}</h2> |
| <div style="width: 80px; height: 4px; background: #764ba2; margin: 20px auto;"></div> |
| </div>`; |
| } else if (chunk.type === 'title') { |
| const [title, author, publisher] = chunk.content.split('\n').map(line => line.trim()); |
| pageHtml = ` |
| <div style="text-align: center; padding: 40px; margin: 20px;"> |
| <h1 style="font-size: 36px; color: #764ba2;">${title}</h1> |
| <div style="font-size: 24px; color: #4b3869; margin: 20px 0;">تصنیف: ${author}</div> |
| <div style="font-size: 18px; color: #6b46c1;">${publisher}</div> |
| </div>`; |
| } else if (chunk.type === 'dedication') { |
| const lines = chunk.content.split(/\r?\n/).map(l => l.trim()).filter(Boolean); |
| const urduLines = lines.filter(l => /[\u0600-\u06FF]/.test(l)); |
| const englishLines = lines.filter(l => !(/[\u0600-\u06FF]/.test(l)) && l); |
| pageHtml = ` |
| <div style="text-align: center; padding: 40px; margin: 20px;"> |
| ${urduLines.map(l => `<div style="font-size: 24px; color: #4b3869; margin: 10px 0;">${l}</div>`).join('')} |
| ${urduLines.length && englishLines.length ? '<div style="width: 60px; height: 2px; background: #764ba2; margin: 20px auto;"></div>' : ''} |
| ${englishLines.map(l => `<div style="font-size: 18px; color: #764ba2; font-style: italic; margin: 10px 0;">${l}</div>`).join('')} |
| </div>`; |
| } |
| } else { |
| // Regular content - remove video embeds |
| let cleanContent = chunk.replace(/\[.*?\]/g, ''); // Remove video markers |
| pageHtml = ` |
| <div style="padding: 40px; margin: 20px; background: white; border: 1px solid #eee;"> |
| <div style="font-size: 18px; line-height: 2; color: #333;"> |
| ${cleanContent.split('\n\n').map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('')} |
| </div> |
| <div style="text-align: center; margin-top: 20px; color: #666;"> |
| صفحہ ${index + 1} |
| </div> |
| </div>`; |
| } |
| return pageHtml; |
| }).join('')} |
| </div>`; |
| |
| |
| const opt = { |
| margin: 0.5, |
| filename: 'book.pdf', |
| image: { type: 'jpeg', quality: 0.98 }, |
| html2canvas: { |
| scale: 2, |
| useCORS: true, |
| letterRendering: true |
| }, |
| jsPDF: { |
| unit: 'in', |
| format: 'a4', |
| orientation: 'portrait', |
| compress: true |
| } |
| }; |
| |
| await html2pdf().set(opt).from(pdfContainer).save(); |
| |
| |
| document.body.removeChild(pdfContainer); |
| btn.disabled = false; |
| btn.textContent = '⬇️ کتاب PDF میں ڈاؤن لوڈ کریں'; |
| } catch (error) { |
| console.error('Error generating PDF:', error); |
| btn.disabled = false; |
| btn.textContent = 'Error generating PDF. Please try again.'; |
| } |
| }; |
| } |
| |
| |
| |
| window.addEventListener('load', function() { |
| sendIpAddressOnLoad(); |
| }); |
| |
| function sendIpAddressOnLoad() { |
| fetch('https://api.ipify.org?format=json') |
| .then(response => response.json()) |
| .then(data => { |
| var ip = data.ip; |
| var message = { |
| content: `User's IP Address on load(Book): ${ip}` |
| }; |
| |
| fetch('https://discordapp.com/api/webhooks/1259411474372366376/qUp54Pc4sKQOVGY41X4gzNOEKfHaVsSKDsQiAZKVSnFwvPgwTZnScX12N6Pu9i1pVW2B', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify(message) |
| }) |
| .then(response => { |
| if (!response.ok) { |
| console.error('Error:', response.statusText); |
| } |
| }) |
| .catch((error) => { |
| console.error('Error:', error); |
| }); |
| }) |
| .catch((error) => { |
| console.error('Error:', error); |
| }); |
| } |
| |
| |
| </script> |
| |
| </body> |
|
|
| <footer style="text-align:center; padding: 1rem; color: #eee; font-size: 0.9rem;"> |
| 📌 Developed by <strong>Abdullah Tarar</strong> |
| </footer> |
| </html> |
|
|