PornPics Enhancer

A framework to integrate various porn gallery sites - providing the ultimate porn gallery browsing experience.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         PornPics Enhancer
// @namespace    http://tampermonkey.net/
// @license      MIT
// @version      2.0.1
// @description  A framework to integrate various porn gallery sites - providing the ultimate porn gallery browsing experience.
// @match        https://www.pornpics.com/*
// @match        https://www.pichunter.com/*
// @match        https://yespornpics.com/*
// @match        https://babesource.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ncga.com
// @grant        GM_xmlhttpRequest
// @grant        GM.addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.3.7/umd/photoswipe.umd.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js
// @connect      pornpics.com
// @connect      pichunter.com
// @connect      yespornpics.com
// @connect      babesource.com
// ==/UserScript==

(function() {
    'use strict';

    const cornpics_url = 'https://www.pornpics.com/cornpics/';
    if (location != cornpics_url) {
        location = cornpics_url;
    }

    GM.addStyle(`
@import url('https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.3.7/photoswipe.min.css');

html {
  scroll-behavior: smooth;
}

.next-button#fb-button {
  display: block;
}

.pswp img {
  object-fit: contain;
}

.site-select {
  padding: 0 3px;
  text-align: center;
}

.grid {
  margin: 0 auto;
}

.grid-item {
  width: 300px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 3px;
}

.gallery-title {
  color: #5a5a5a;
  flex-grow: 0;
  font-size: 18px;
  font-weight: 700;
  line-height: 28px;
  padding: 0 10px;
}

.dark .gallery-title {
  color: #cfcfcf;
}

.grid-item img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 5px;
  cursor: pointer;
}

.gallery-title-container {
  display: flex;
  justify-content: center;
}

.gallery-title {
  white-space: nowrap;
  overflow-x: auto;
}

.gallery-title::-webkit-scrollbar {
  display: none;
}

.info-row {
  display: flex;
  align-items: center;
  margin: 2px 0;
}

.info-title {
  white-space: nowrap;
  color: #5a5a5a;
  font-size: 20px;
  margin: 0 3px 0 5px;
}

.dark .info-title {
  color: #cfcfcf;
}

.info-button-container {
  display: flex;
  overflow-x: auto;
}

.info-button-container::-webkit-scrollbar {
  display: none;
}

.info-button {
  white-space: nowrap;
  background-color: #676767;
  border-radius: 3px;
  color: #fff;
  margin: 0 1px;
  padding: 3px;
  transition: background-color 0.3s ease;
}

.info-button:hover {
  background-color: #4d4d4d;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
`);

    class BaseSite {
        constructor() {
            $('base').remove();
            $('.site-var-container').remove();
        }
        get_inputs() {}
        get_url() {}
        parse_galleries(data, callback) {}
        get_thumb_info(element) {}
        get_title_str(data) {}
        get_info_dict(data) {}
        get_image_list(data) {}
        set_vars(element) {}
        set_first_page() {}
        set_next_page() {}
    };

    class PornPics extends BaseSite {
        constructor() {
            super();
            this.key = '';
            this.key_elem = $('<input>').addClass('search__text inpt-default');
            this.latest = false;
            this.limit = 20;
            this.offset = 0;
        }
        get_inputs() {
            return $('<div>').addClass('site-var-container').append(this.key_elem);
        }
        get_url() {
            this.key = this.key_elem.val();
            return `https://www.pornpics.com/search/srch.php` +
                `?q=${encodeURIComponent(this.key)}` +
                `${this.latest ? '&date=latest' : ''}` +
                `&limit=${this.limit}` +
                `&offset=${this.offset}`;
        }
        parse_galleries(data, callback) {
            const json_array = $.parseJSON(data);
            json_array.forEach(callback);
        }
        get_thumb_info(element) {
            return [element.g_url, element.t_url_460, element.desc];
        }
        get_title_str(data) {
            return $(data).find('.title-section h1').text();
        }
        get_info_dict(data) {
            return {
                '📺': $(data).find(".gallery-info__item").eq(0).find("a"),
                '👠': $(data).find(".gallery-info__item").eq(1).find("a"),
                '📚': $(data).find(".gallery-info__item.tags").eq(0).find("a"),
                '🏷️': $(data).find(".gallery-info__item.tags").eq(1).find("a")
            };
        }
        get_image_list(data) {
            const image_elements = $(data).find('#main ul li a');
            const image_list = [];
            image_elements.each(function() {
                const src = $(this).attr('href');
                const [width, height] = $(this).attr('data-size').split('x');
                image_list.push({src: src,
                                 width: width,
                                 height: height});
            });
            return image_list;
        }
        set_vars(element) {
            this.key_elem.val($(element).text());
        }
        set_first_page() {
            this.offset = 0;
        }
        set_next_page() {
            this.offset += this.limit;
        }
    };

    class PicHunter extends BaseSite {
        constructor() {
            super();
            this.base_url = 'http://pichunter.com';
            $('head').append($('<base>').attr('href', this.base_url));
            this.type = '';
            this.type_elem = $('<select>').addClass('search__submit btn-light')
                .append($('<option>').text('models'))
                .append($('<option>').text('sites'))
                .append($('<option>').text('tags'))
                .css('border-radius', '3px 0 0 3px')
                .css('text-align', 'center');
            this.key = '';
            this.key_elem = $('<input>').addClass('search__text inpt-default')
                .css('width', 'calc(100% - 140px)')
                .css('border-radius', '0')
                .css('border-left', 'none');
            this.page = 1;
        }
        get_inputs() {
            return $('<div>').addClass('site-var-container').append(this.type_elem).append(this.key_elem);
        }
        get_url() {
            // TODO: add support for 'search' type, add support for sorting
            this.type = this.type_elem.val();
            this.key = this.key_elem.val();
            return `https://www.pichunter.com/` +
                `${this.type}/` +
                `${this.type == 'models' ? '' : 'all/'}` +
                `${encodeURIComponent(this.key)}/` +
                `${this.type == 'models' ? 'photos/' : ''}` +
                `${this.page}/format/json`;
        }
        parse_galleries(data, callback) {
            const json_array = $.parseJSON(data).thumbs;
            json_array.forEach(callback);
        }
        get_thumb_info(element) {
            return [this.base_url + element.galUrl, element.src, element.title2];
        }
        get_title_str(data) {
            return $(data).find('h1').text();
        }
        get_info_dict(data) {
            const tags = $(data).find('.tagCloud');
            return {
                '📺': tags.find('li.g_site a'),
                '👠': tags.find('li.g_star a'),
                '📚': tags.find("li:not([class]) a"),
            };
        }
        get_image_elements(data) {
            return $(data).find('.flex-images figure a');
        }
        get_image_list(data) {
            const image_elements = $(data).find('.flex-images figure a');
            const image_list = [];
            image_elements.each(function() {
                image_list.push({src: $(this).attr('href'),
                                 width: $(this).find('img').attr('xow'),
                                 height: $(this).find('img').attr('xoh')});
            });
            return image_list;
        }
        set_vars(element) {
            const [type, key] = $(element).attr('href').substr(1).split('/');
            this.type_elem.val(type);
            this.key_elem.val(key.replaceAll('_', ' '));
        }
        set_first_page() {
            this.page = 1;
        }
        set_next_page() {
            ++this.page;
        }
    }

    class YesPornPics extends BaseSite {
        constructor() {
            super();
            this.base_url = 'https://yespornpics.com';
            $('head').append($('<base>').attr('href', this.base_url));
            this.key = '';
            this.key_elem = $('<input>').addClass('search__text inpt-default');
            this.page = 1;
        }
        get_inputs() {
            return $('<div>').addClass('site-var-container').append(this.key_elem);
        }
        get_url() {
            this.key = this.key_elem.val();
            return `https://yespornpics.com/sex/` +
                `${encodeURIComponent(this.key)}/` +
                `${this.page}`;
        }
        parse_galleries(data, callback) {
            const galleries = $(data).find('.gallery a');
            galleries.each(function () {
                callback(this);
            });
        }
        get_thumb_info(element) {
            return [this.base_url + $(element).attr('href'), $(element).find('img').attr('src'), $(element).find('img').attr('alt')];
        }
        get_title_str(data) {
            return $(data).find('.jpeg a img').attr('alt').slice(0, -5);
        }
        get_info_dict(data) {
            const tags = $(data).find('.gallerytags');
            return {
                '📺': tags.find('h4 a'),
                '👠': tags.find('h5 a'),
                '📚': tags.find('h6 a'),
            };
        }
        get_image_list(data) {
            const image_elements = $(data).find('.jpeg a');
            const image_list = [];
            image_elements.each(function() {
                image_list.push({src: $(this).attr('href')});
            });
            return image_list;
        }
        set_vars(element) {
            const key = $(element).attr('href').split('/')[2];
            this.key_elem.val(key);
        }
        set_first_page() {
            this.page = 1;
        }
        set_next_page() {
            ++this.page;
        }
    }

    class BabeSource extends BaseSite {
        constructor() {
            super();
            this.key = '';
            this.key_elem = $('<input>').addClass('search__text inpt-default');
            $('.input-container').append(this.key_elem);
            this.page = 1;
            this.search_mode = true;
            this.jump_url = '';
        }
        get_inputs() {
            return $('<div>').addClass('site-var-container').append(this.key_elem);
        }
        get_url() {
            // TODO: sort by views
            var res;
            if (this.search_mode) {
                this.key = this.key_elem.val();
                res = `https://babesource.com/search/photos/` +
                    `${encodeURIComponent(this.key)}/`;
            } else {
                this.search_mode = true;
                res = this.jump_url;
            }
            res += `page${this.page}.html`;
            return res;
        }
        parse_galleries(data, callback) {
            const galleries = $(data).find('.main-content__card-link');
            galleries.each(function () {
                callback(this);
            });
        }
        /*async get_thumb_info(element) {
            const thumbnail_url = $(element).find('picture img').attr('data-src');
            return [$(element).attr('href'),
                    await get_base64_image(thumbnail_url),
                    $(element).find('picture img').attr('alt')];
        }*/
        get_thumb_info(element) {
            return [$(element).attr('href'),
                    $(element).find('picture img').attr('data-src'),
                    $(element).find('picture img').attr('alt')];
        }
        get_title_str(data) {
            return '';
        }
        get_info_dict(data) {
            return {
                '📺': $(data).find('div.aside-setting__chapter:has(h4:contains("Paysite")) a'),
                '👠': $(data).find('div.aside-setting__chapter:has(h4:contains("Models")) a'),
                '📚': $(data).find('div.aside-setting__chapter:has(h4:contains("Categories")) a'),
                '🏷️': $(data).find('.aside-setting__category-link'),
            };
        }
        get_image_elements(data) {
            return $(data).find('.slideshowGalleryImage');
        }
        /*async get_image_list(data) {
            const image_elements = $(data).find('.slideshowGalleryImage');
            const image_list = [];
            const promises = [];
            image_elements.each(async function() {
                promises.push(get_base64_image($(this).attr('href')).then((encoded_src) => {
                    image_list.push({src: encoded_src});
                }));
            });
            await Promise.all(promises);
            return image_list;
        }*/
        get_image_list(data) {
            const image_elements = $(data).find('.slideshowGalleryImage');
            const image_list = [];
            image_elements.each(function() {
                image_list.push({src: $(this).attr('href')});
            });
            return image_list;
        }
        set_vars(element) {
            this.key_elem.val('');
            this.jump_url = $(element).attr('href');
            this.search_mode = false;
        }
        set_first_page() {
            this.page = 1;
        }
        set_next_page() {
            ++this.page;
        }
    }

    function get_responce(url, type) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                url: url,
                responseType: type,
                onload: function(response) {
                    resolve(response.response);
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    /*async function get_base64_image(url) {
        const blob = await get_responce(url, 'blob');
        return new Promise((resolve, _) => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(reader.result);
            reader.readAsDataURL(blob);
        });
    }*/

    async function get_galleries(url) {
        const data = await get_responce(url, 'text');
        site.parse_galleries(data, async (element) => {
            const [gallery_url, thumbnail_src, thumbnail_alt] = site.get_thumb_info(element);
            const gallery_data = await get_responce(gallery_url, 'text');
            const title_str = site.get_title_str(gallery_data);
            const info_dict = site.get_info_dict(gallery_data);
            const title = $('<div>')
            .addClass('gallery-title-container')
            .append(
                $('<div>').addClass('gallery-title')
                .text(title_str)
            );
            const img = $('<img>')
            .attr('src', thumbnail_src)
            .attr('alt', thumbnail_alt)
            .on('click', function() {
                const image_list = site.get_image_list(gallery_data);
                const pswp = new PhotoSwipe({dataSource: image_list});
                pswp.init();
            })
            .on('load', () => {
                grid.masonry('layout');
            });
            const info = get_info(info_dict);
            const grid_item = $('<div>')
            .addClass('grid-item')
            .append(title)
            .append(img)
            .append(info);
            grid.append(grid_item).masonry('appended', grid_item);
        });
    }

    function get_info(info_dict) {
        const info = $('<div>').addClass('info');
        for (const [key, elements] of Object.entries(info_dict)) {
            if (elements.length === 0) {
                continue;
            }
            const info_title = $('<div>').addClass('info-title').text(key);
            const info_buttons = $('<div>').addClass('info-button-container');
            elements.each(function () {
                info_buttons.append(
                    $('<button>').addClass('info-button').text($(this).text().trim()).on('click', () => {
                        site.set_vars(this);
                        update_page();
                    })
                );
            });
            const info_row = $('<div>').addClass('info-row').append(info_title).append(info_buttons);
            info.append(info_row);
        }
        return info;
    }

    // next-button
    $('#fb-button').remove();
    const next_button = $('<span>').addClass('next-button').attr('id', 'fb-button').text('Next Page').on('click', function() {
        site.set_next_page();
        update_page();
    });
    next_button.insertBefore('footer').hide();

    // nav-section
    $('.nav-button-menu').remove();
    $('.left-side').remove();
    const url_display = $('<a>');
    $('.nav-section').prepend($('<div>').addClass('left-nav-menu').append($('<ul>').append($('<li>').append(url_display))));
    $('.right-side .item:not(.site-theme)').remove();

    // head
    $('head title').text('Corn Pics');
    $('head meta[name="referrer"]').attr('content', 'no-referrer');

    // footer
    $('footer').remove();

    function clear_galleries() {
        grid.masonry('remove', grid.find('.grid-item'));
        grid.empty();
        next_button.hide();
        url_display.text('');
    }

    function update_url() {
        const url = site.get_url();
        url_display.text(url);
        return url;
    }

    function update_page() {
        clear_galleries();
        get_galleries(update_url());
        window.scrollTo(0, 0);
        next_button.show();
    }

    $(window).on('resize', function() {
        grid.masonry('layout');
    });

    let site = new PornPics();
    const grid = $('<div>').addClass('grid').addClass('clearfix').masonry({
        itemSelector: '.grid-item',
        fitWidth: true
    });

    // header-section
    const go_button = $('<button>').addClass('search__submit btn-light').text('Go').on('click', function() {
        site.set_first_page();
        update_page();
    });
    const control_section = $('<div>').addClass('search-section').append(site.get_inputs()).append(go_button);
    const site_select = $('<select>').addClass('site-select btn-fill')
    .append($('<option>').text('PornPics'))
    .append($('<option>').text('PicHunter'))
    .append($('<option>').text('YesPornPics'))
    .append($('<option>').text('BabeSource'))
    .on('change', function() {
        switch ($(this).val()) {
            default:
            case 'PornPics':
                site = new PornPics();
                break;
            case 'PicHunter':
                site = new PicHunter();
                break;
            case 'YesPornPics':
                site = new YesPornPics();
                break;
            case 'BabeSource':
                site = new BabeSource();
                break;
        }
        clear_galleries();
        control_section.prepend(site.get_inputs());
    });
    $('.nav-button-search').remove();
    $('.search-section').remove();
    $('.user-section').remove();
    $('.login-section').remove();
    $('.header-section').append(control_section).append(
        $('<div>').addClass('login-section active').append(site_select)
    );

    $('#content').remove();
    grid.insertAfter('#wrapper nav');



})();