yaqwsx / jlcparts

Better parametric search for components available for JLC PCB assembly
https://yaqwsx.github.io/jlcparts/
MIT License
538 stars 51 forks source link

feature request: download ecad models #130

Open KJ7LNW opened 4 months ago

KJ7LNW commented 4 months ago

It would be great if jlcparts can provide a link to download easyeda models.

You are half way there with EasyEDAFootprintScraper, though I know it expects python, so not sure how that could work in a JS-based UI. Still, if you can come up with a way to "click-and-download" ecad models from your jlcparts engine (eg, hosted CGI somewhere), that would be awesome.

Cheers, and thanks for all the great work on these projects.

-Eric

KJ7LNW commented 4 months ago

(I just realized that EasyEDAFootprintScraper is recommended to be replaced with easyeda2kicad)

I'm not sure what the capabilities for github.io are for backend calls, but easyeda2kicad is pretty easy. Something like this might work:

pip install easyeda2kicad

# pseudo-CGI-shell-code: replace sanitize_get_string($_GET["LCSC_ID"]) with 
# something to prevent shell injection, and then return a .zip of the content:
easyeda2kicad --3d --debug --full --lcsc_id=sanitize_get_string($_GET["LCSC_ID"])

If you can export ecad models, then it could pair nicely with KiCad Imparter:

KJ7LNW commented 4 months ago

Here is a POC:

~]$ unzip -l C264536.zip
Archive:  C264536.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     1376  2024-07-12 15:49   LED1206-R-RD.kicad_mod
     2510  2024-07-12 15:49   part.kicad_sym
        0  2024-07-12 15:49   part.3dshapes/
    36430  2024-07-12 15:49   part.3dshapes/LED1206_Inf.wrl
   387779  2024-07-12 15:49   part.3dshapes/LED1206_Inf.step
---------                     -------
   428095                     5 files

This is the code, in Perl:

#!/usr/bin/perl

use strict;
use warnings;
use CGI::Fast;
use File::Temp qw/tempdir/;
use File::Copy qw/move/;
use Cwd qw(cwd getcwd);
use Data::Dumper;

$ENV{PATH}="$ENV{PATH}:/bin:/usr/bin:/home/eda_ewheeler_org/.local/bin";

BEGIN {
    open(STDERR, ">>/home/eda_ewheeler_org/easyeda2kicad.log");
}
open(LOG, ">>/home/eda_ewheeler_org/easyeda2kicad.log");

my $easyeda2kicad = '/home/eda_ewheeler_org/.local/bin/easyeda2kicad';
my $cachedir = '/home/eda_ewheeler_org/easyeda-cache';
mkdir($cachedir);

#download_part('C2040'); exit;

while (my $q = CGI::Fast->new)
{
    my ($zip, $dir);

    my $lcsc = $q->param("lcsc"); 
    $lcsc =~ tr/A-Za-z0-9_.-//cd;
    if (!defined($lcsc) || !length($lcsc))
    {
        print "Content-type: text/html\r\n\r\n";

        print_usage("Unknown LCSC_ID\n");
        next;
    }

    if (0 &&  -s "$cachedir/$lcsc.zip")
    {
        $zip = "$cachedir/$lcsc.zip";
    }
    else
    {
        ($dir, $zip) = download_part($lcsc);

        if (-d $dir && -s $zip)
        {
            move($zip, "$cachedir/");

            # store the file as the model name, but symlink the LCSC_ID
            # for future reference:
            my $basename = $zip;
            $basename =~ s/^.*\///;
            symlink("$basename", "$cachedir/$lcsc.zip");

            rmdir($dir) if $dir;
            $zip = "$cachedir/$basename";
        }
    }

    if (defined $zip && -s $zip)
    {
        # If a symlink is accessed, use the linked filename:
        my $basename = $zip;
        if (-l $zip)
        {
            $basename = readlink($zip);
        }
        $basename =~ s/^.*\///;

        print "Content-type: x-application/zip\r\n";
        print "Content-Disposition: attachment; filename=\"$basename\"\r\n";
        print "\r\n";

        catfile($zip);
    }
    else
    {
        print "Content-type: text/html\r\n\r\n";

        #print "Unknown LCSC_ID zip=$zip dir=$dir";
        print_usage("Unknown LCSC_ID");
    }

}

sub download_part
{
    my $lcsc = shift;
    $lcsc =~ tr/A-Za-z0-9_.-//cd;

    my $dir = tempdir(CLEANUP => 1);

    popen($easyeda2kicad, '--full', '--output', "$dir/part", "--lcsc_id=$lcsc");

    if (! -s "$dir/part.kicad_sym")
    {
        return undef;
    }

    my $h = readsym("$dir/part.kicad_sym");
    print LOG Dumper($h);
    my $name = $h->{name};
    my $part = $h->{part};

    if (!defined($name) || !length($name))
    {
        return undef;
    }

    #print "part: $name\n";

    rename("$dir/part.pretty/$part.kicad_mod",  "$dir/$part.kicad_mod");
    rmdir("$dir/part.pretty");

    my $cwd = getcwd();
    chdir($dir);
    popen(qw/zip -9 -r --move/, "$dir/$name.zip", ".");
    print LOG popen(qw/unzip -l/, "$dir/$name.zip");
    chdir($cwd);

    return ($dir, "$dir/$name.zip");
}

sub popen
{
    my @args = @_;

    print LOG "popen: " . join(' ', @args) . "\n";

    my $in;
    my $ret;
    if (!open($in, "-|")) {
        exec(@args);
    }

    while (defined(my $line = <$in>)) {
        $ret //= '';
        $ret .= $line;
    }
    close($in);
    #print "popen: $ret\n";

    return $ret;
}

sub readsym
{
    my $p = shift;

    open(my $in, $p) or die "$p: $!";

    my $part;
    my $symbol;
    my $lcsc;
    while ((!$lcsc || !$part || !$symbol) && defined(my $line = <$in>))
    {
        chomp $line;
        if (!$part && $line =~ /"part:([^"]+)"/)
        {
            $part = $1;
        }

        if (!$symbol && $line =~ /symbol "([^"]+)"/)
        {
            $symbol = $1;
        }

        if (!$lcsc && $line =~ /"LCSC Part"/)
        {
            $lcsc = <$in>; # on the next line
            chomp($lcsc);

            $lcsc =~ s/^\s+|\s+$//gs;

            $lcsc =~ tr/"//d;
        }
    }

    close($in);

    $symbol =~ s/\s+/_/g;
    $part =~ s/\s+/_/g;

    my $name = "${symbol}_${lcsc}_$part";
    print LOG "*** $name\n";

    return { part => $part, symbol => $symbol, lcsc => $lcsc, name => $name };
}

sub catfile
{
    my $fn = shift;

    #print "fn: $fn\n"; return;
    open(my $in, $fn) or die "$fn: $!";

    my $buf;
    while (sysread($in, $buf, 1024))
    {
        syswrite(\*STDOUT, $buf);
    }
}

sub print_usage
{
    my $m = shift;
    print qq{
    <html><body>
        <form><h1>$m</h1>
        LCSC: <input name=lcsc />
        <input type=submit>
        </form>

    </body></html>
    };
}
KJ7LNW commented 1 month ago

I just updated the perl LCSC sample downloader code above, it works great with the latest version of the Kicad Imparter plugin. Imparter works with easyeda2kicad-generated archives after this commit: https://github.com/Steffen-W/Import-LIB-KiCad-Plugin/commit/792ec670d0228af5ce16e8f8c28312383b55d0a4

Just visit this link and enter the LCSC "C" number. https://eda.ewheeler.org/easyeda2kicad.fcgi

Would be neat if jlcparts could link to parts as https://eda.ewheeler.org/easyeda2kicad.fcgi?lcsc=C2040 for easy download, or feel free to self-host or re-implement the idea since it is quite simple.