Greasy Fork is available in English.

PyPI package name and version fetcher

Fetches all the package names displayed in a PyPI search along with their last version and copies it to clipboard. Crafted for copy all OCA metapackages when searching for "odoo14-addons-oca"

// ==UserScript==
// @name         PyPI package name and version fetcher
// @version      1
// @namespace    https://coopdevs.coop
// @license AGPL-3
// @description  Fetches all the package names displayed in a PyPI search along with their last version and copies it to clipboard. Crafted for copy all OCA metapackages when searching for "odoo14-addons-oca"
// @description:es-ES Busca todos los nombres de paquetes que se muestran en una búsqueda de PyPI junto con su última versión y los copia al portapapeles. Hecho para copiar todos los metapaquetes OCA al buscar "odoo14-addons-oca"
// @description:en-US Fetches all the package names displayed in a PyPI search along with their last version and copies it to clipboard. Crafted for copy all OCA metapackages when searching for "odoo14-addons-oca"
// @description:pt-BR Busca todos os nomes de pacotes exibidos em uma pesquisa do PyPI, juntamente com sua última versão e os copia para a área de transferência. Feito para copiar todos os metapacotes OCA ao pesquisar por "odoo14-addons-oca"
// @description:fr-FR Recherche tous les noms de paquets affichés dans une recherche PyPI ainsi que leur dernière version et les copie dans le presse-papiers. Fabriqué pour copier tous les méta-paquets OCA lors de la recherche de "odoo14-addons-oca"
// @description:ru-RU Ищет все имена пакетов, отображаемые в поиске PyPI, а также их последнюю версию, и копирует их в буфер обмена. Сделано для копирования всех метапакетов OCA при поиске «odoo14-addons-oca»
// @author       laicoop
// @match        https://pypi.org/search/*
// ==/UserScript==
/* jshint esversion: 8 */

(function() {
    
  'use strict';

  let currentPage = 1;
  let maxPages = 1;

function addButton() {
    console.log("Button added");
    const button = document.createElement('div');
    button.className = 'projects';
    button.style = 'text-align: center; border-radius: 5px;';
    button.style.width = '30px';
    button.style.height = '30px';
    button.title = 'Click to copy version and name from all packages to clipboard';
    button.innerHTML = '📥';
    button.addEventListener('click', fetchAllPackages);

    const container = document.querySelector('.search-form');
    container.appendChild(button);
  }

  async function fetchAllPackages() {
    console.log(`Fetching all packages, currently on page ${currentPage}`);
    await fetchPackages();
  }

  async function fetchPackages() {
    const foundPackagesElement = document.querySelector(".split-layout--table > div:nth-child(1) > p:nth-child(1) > strong:nth-child(1)");
    const foundPackages = parseInt(foundPackagesElement.innerText.replace(",", ""));
    const versioned_pkgs = [];
    console.log("fetchPackages...");
    console.log(`Found pagination with ${maxPages} pages`);
    // Loop through each page and get the package names and versions
    for (let cpage = 1; cpage <= maxPages; cpage++) {
      console.log(`Fetching page ${cpage}...`);
      const url = `${window.location.href}&page=${cpage}`;
      const response = await fetch(url);
      const html = await response.text();
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, 'text/html');
      const pkgs = doc.querySelectorAll('.package-snippet__title');
      pkgs.forEach((currentValue, currentIndex, listObj) => {
        versioned_pkgs.push(pkgs[`${currentIndex}`].querySelector('.package-snippet__name').innerHTML + "==" + pkgs[`${currentIndex}`].querySelector('.package-snippet__version').innerHTML);
      });
    }

    // Copy the package names and versions to the clipboard
    const packageList = versioned_pkgs.join('\n');

    console.log(`Copied ${versioned_pkgs.length} of ${foundPackages}`);
    if (versioned_pkgs.length < foundPackages) {
      alert(`Not all packages copied (${versioned_pkgs.length}/${foundPackages}). Try again.`)
    }

    navigator.clipboard.writeText(packageList)
        .then(() => console.log('Package names and versions copied to clipboard'))
        .catch(error => console.error(`Error copying package names and versions to clipboard: ${error}`));
    alert(` ${versioned_pkgs.length} packages copied. Paste it in your requirements.txt file`)
  }
  function init() {
      console.log('Initializing script');
      const pagination = document.querySelector('.button-group--pagination');
      if (pagination) {
        const buttons = pagination.querySelectorAll('.button-group__button');
        buttons.forEach(button => {
          const text = button.textContent.trim();
          const page = parseInt(text, 10);
          if (!isNaN(page) && page > maxPages) {
            maxPages = page;
          }
        });
        console.log(`Found pagination with ${maxPages} pages`);
      } else {
        console.log('No pagination found, defaulting to first page');
      }
      addButton();

      const savedCurrentPage = GM_getValue('currentPage', 1);
      if (savedCurrentPage > 1) {
        console.log(`Restoring saved current page: ${savedCurrentPage}`);
        currentPage = savedCurrentPage;
      }
    }

  init();
})();