async function searchResults(keyword) {
    const results = [];
    let pageNumber = 1;
    let hasMore = true;

    try {
        while (hasMore) {
            const headers = {
                "next-action": await extractNextActionIdentifier(),
                "Content-Type": "text/plain"
            };
            console.log(JSON.stringify(headers));
            const postData = `[${pageNumber},"${keyword}",""]`;
            const response = await fetchv2("https://mapple.site/audiobooks?search=" + encodeURIComponent(keyword), headers, "POST", postData);
            const text = await response.text();
            console.log("Page " + pageNumber + " response:", text);
            
            const lines = text.split('\n').filter(line => line.trim());
            let foundBooks = false;
            
            for (const line of lines) {
                try {
                    const data = JSON.parse(line.substring(line.indexOf('{')));
                    
                    if (data.books && Array.isArray(data.books)) {
                        foundBooks = true;
                        for (const book of data.books) {
                            let title = book.title || "Untitled";
                            title = title.replace(/\s+free\s+online\s*$/i, '')
                                         .replace(/\s+audiobook\s+free\s+online\s*$/i, '')
                                         .replace(/\s+audiobook\s+free\s*$/i, '')
                                         .replace(/\s+free\s+audiobook\s*$/i, '')
                                         .replace(/\s+free\s*$/i, '')
                                         .replace(/\s+\(free\s*\)\s*$/i, '')
                                         .replace(/\s+free\s+read\s+/i, ' read ')
                                         .replace(/\s+\(.*?free.*?\)\s*$/i, '')
                                         .replace(/\s+audiobook\s*$/i, '')
                                         .trim();
                            
                            results.push({
                                title: title,
                                image: book.image || "",
                                href: "https://mapple.site/listen/" + (book.$id || "") + "/" + encodeURIComponent(book.title || "untitled")
                            });
                        }
                        hasMore = data.hasMore === true;
                        console.log("hasMore: " + hasMore);
                    }
                } catch (e) {
                }
            }
            
            if (!foundBooks) {
                hasMore = false;
            }
            
            pageNumber++;
        }

        return JSON.stringify(results);
    } catch (err) {
        console.log("Search error:", err);
        return JSON.stringify([{
            title: "Error",
            image: "Error",
            href: "Error"
        }]);
    }
}

async function extractDetails(url) {
    try {
        return JSON.stringify([{
            description: "",
            aliases: "",
            airdate: ""
        }]);
    } catch (err) {
        return JSON.stringify([{
            description: "Error",
            aliases: "Error",
            airdate: "Error"
        }]);
    }
}

async function extractEpisodes(url) {
    const results = [];
    try {
        const fetchResponse = await fetchv2(url, {
            "rsc": "1"
        });
        const fetchText = await fetchResponse.text();
        
        const audioItemsMatch = fetchText.match(/"audio_items":\[([^\]]+)\]/);
        
        if (audioItemsMatch && audioItemsMatch[1]) {
            const audioItemsStr = audioItemsMatch[1];
            const urlMatches = audioItemsStr.match(/"(https?:\/\/[^"]+)"/g);
            
            if (urlMatches) {
                for (let i = 0; i < urlMatches.length; i++) {
                    const audioUrl = urlMatches[i].replace(/"/g, '');
                    results.push({
                        href: audioUrl,
                        number: i + 1,
                        title: `Chapter ${i + 1}`
                    });
                }
            }
        }

        return JSON.stringify(results);
    } catch (err) {
        console.log("Episodes error:", err);
        return JSON.stringify([{
            href: "Error",
            number: "Error",
            title: "Error"
        }]);
    }
}

async function extractStreamUrl(url) { 
    try {
                const headers = {
                    "Host": "ipaudio.club",
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0",
                    "Accept": "*/*",
                    "Accept-Language": "en-US,en;q=0.5",
                    "Accept-Encoding": "gzip, deflate, br, zstd",
                    "Connection": "keep-alive",
                    "Sec-Fetch-Dest": "empty",
                    "Sec-Fetch-Mode": "no-cors",
                    "Sec-Fetch-Site": "cross-site",
                    "Priority": "u=4",
                    "Pragma": "no-cache",
                    "Cache-Control": "no-cache"
                };
                
                const streams = [{
                    title: `Audio Stream 1`,
                    streamUrl: url,
                    headers: headers
                }];
                
                return JSON.stringify({
                    streams: streams,
                    subtitle: ""
                });

    } catch (err) {
        console.log("Stream error:", err);
        return JSON.stringify({
            streams: [{
                title: "Error",
                streamUrl: "",
                headers: {}
            }],
            subtitle: ""
        });
    }
}

async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) {
    try {
        return await fetchv2(
            url,
            options.headers ?? {},
            options.method ?? 'GET',
            options.body ?? null,
            true,
            options.encoding ?? 'utf-8'
        );
    } catch(e) {
        try {
            return await fetch(url, options);
        } catch(error) {
            return null;
        }
    }
}

async function extractNextActionIdentifier() {
    const htmlResponse = await soraFetch(atob("aHR0cHM6Ly9tYXBwbGUuc2l0ZS9hdWRpb2Jvb2tz"));
    const htmlText = await htmlResponse.text();

    const pageMatch = htmlText.match(/<script[^>]*src="([^"]*app\/audiobooks\/page-[^"]*\.js)"[^>]*><\/script>/);
    
    if (!pageMatch || !pageMatch[1]) {
        throw new Error("error 1");
    }

    const beforePageMatch = htmlText.match(/<script[^>]*src="([^"]*\.js)"[^>]*><\/script><script[^>]*src="[^"]*app\/audiobooks\/page-[^"]*\.js"/);
    
    if (!beforePageMatch || !beforePageMatch[1]) {
        throw new Error("error 2");
    }

    let targetUrl = beforePageMatch[1];
    if (targetUrl.startsWith('/_next/')) {
        targetUrl = 'https://mapple.site' + targetUrl;
    } else if (!targetUrl.startsWith('http')) {
        targetUrl = 'https://mapple.site/' + targetUrl;
    }

    try {
        const response = await soraFetch(targetUrl);
        const text = await response.text();

        let actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"[^"]*"getStreamUrl/);
        if (!actionMatch) {
            actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"/);
        }
        if (!actionMatch) {
            actionMatch = text.match(/"([a-f0-9]{40,})"[^"]*"getStreamUrl/);
        }
        
        if (actionMatch && actionMatch[1]) {
            return actionMatch[1];
        }
    } catch (e) {
        throw new Error("error 3: " + e);
    }

    throw new Error("error 4");
}
