#!/usr/bin/perl -w

# (c) Christian Wolff 2024-12-01

# Determines the true cost of each item in an order of multiple items.
# Spreads out communal cost (shipping, taxes, discounts, etc.) proportionally.
# Input File: Open order confirmation email, Select All, Copy, Paste into text file
# Output: TAB-separated items, matching the data base format of the dvdlist CGI script
#   <Title>TAB<Release>TAB<Duration>TAB<Aspect ratio>TAB<Discs>TAB<Std./Reg.>TAB<Language / Audio Format>TAB<Subtitles>TAB<IMDB>TAB<Delivery Date>TAB<Cost>TAB<Amazon ASIN>TAB<Release URL>


use Math::Round qw/round/;
use URI::Escape;
use HTML::Entities;
use JSON;

binmode STDOUT, ':utf8';

my $wget = 'wget -t 3 -T 10';

my $useragent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:143.0) Gecko/20100101 Firefox/143.0';
#my $useragent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0';
#my $useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax)';

my $db_header_row = "Title\tRelease\tDuration\tAspect ratio\tDiscs\tStd./Reg.\tLanguage / Audio Format\tSubtitles\tInfo / IMDB\tDate Added\tCost\tAmazon ASIN\tRelease URL\n";

my $now = time();
my ($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = gmtime($now);
$year += 1900;
$month++;
my $eta = sprintf("%04d-%02d-%02d", ($month == 12) ? $year + 1 : $year, ($month == 12) ? 1 : $month + 1, 99);

# Sums up one item price of an order, adjusted for a total that includes shipping, taxes, etc. with error accumulation
sub sum_price
{
    my ($price, $subtotal, $total, $prev_error) = @_;
    
    my $cost = $price / $subtotal * $total + $prev_error;
    my $cents = round($cost * 100);
    my $dollars = $cents / 100;
    my $error = $cost - $dollars;
    
    return ($dollars, $error);
}

# Sums up the item prices of an order, determining a subtotal and the true cost of each item
# Parameters:
#   total: Final total, the price paid for the order
#   prices: Array of all item prices of the order
sub sum_items
{
    my $total = shift;
    my @prices = @_;
    my @costs = ();
    
    # Consecutively determines subtotal (raw cost) of all item prices of an order
    my $subtotal = 0;
    foreach my $price (@prices) {
        # Add one price to a running subtotal
        #print "Adding '${price}' for '${total}'\n";
        $subtotal += $price;
    }
    printf "\nSummary:\n  Subtotal of all items: \$%.2f\n", $subtotal;
    printf "  Total of order(s): \$%.2f\n", $total;
    printf "  True cost factor: %.8f\n\n", $total / $subtotal;
    
    # Determine fair cost of all items, with regard to total paid
    my ($cost, $error) = (0, 0);
    my $n = 0;
    foreach my $price (@prices) {
        ($cost, $error) = sum_price($price, $subtotal, $total, $error);
        $n++;
        printf "  Item #${n}: \$%.2f --> \$%.2f, remaining error: \$%.8f\n", $price, $cost, $error;
        push(@costs, $cost);
    }
    
    if (($error <= -0.01) || ($error >= 0.01)) {
        printf "WARNING: remaining error = %.8f\n", $error;
    }
    
    return @costs;
}


#<div class="global-search__results-film-container" data-search-results-film-container="">
#
#<div class="global-search__film-result" data-search-results-film-item="">
#<a class="global-search__film-result-link" href="https://www.criterion.com/films/165-seven-samurai">
#<img class="global-search__result-image" src="https://s3.amazonaws.com/criterion-production/films/9d76c0395d994bbb7f9d6f7573796046/cThjxLEYjspgZR4c4TW7itGGxqawj8_original.jpg" alt="Seven Samurai">
#<figcaption class="global-search__result-caption">
#<span class="global-search__result-title">Seven Samurai</span>
#<span class="global-search__result-director">Akira Kurosawa</span>
#</figcaption>
#</a>
#</div>


# https://api.swiftype.com/api/v1/public/engines/search.json?callback=jQuery31007466145880065335_1733503549618&q=Seven%20Samurai&engine_key=47z2a8Ae-x_co8MUSgoe&page=1&per_page=100&filters%5Bpage%5D%5Btype%5D%5B%5D=Film&filters%5Bpage%5D%5Btype%5D%5B%5D=Box%20Set&spelling=strict&_=1733503549619
# 
#/**/jQuery31007466145880065335_1733503549618(
#	{
#		"record_count":100,
#		"records":
#		{
#			"page":
#			[
#				{
#					"sections":
#					[
#						"Seven Samurai","Film Info","4K UHD + BLU-RAY SPECIAL EDITION FEATURES","Purchase Options","Collector's Sets","Digital Options","Cast","Credits","Related Films","Current","Three Reasons: Seven Samurai","A Time of Honor: Seven Samurai and Sixteenth-Century Japan","Wendell B. Harris Jr.\u2019s Top 10","Rashaad Ernesto Green\u2019s Top 10","Who\u2019s That Man? Mifune at 100","Adventures in Moviegoing with Kareem Abdul-Jabbar","Akira Kurosawa","More Akira Kurosawa View All","Shop the Collection","The Criterion Channel","Our Mission","Contact Us","Privacy Policy","My Criterion","FAQ","Search"
#					],
#					"title":"Seven Samurai",
#					"type":"Film",
#					"original_title":"Shichinin no samurai",
#					"external_id":"b55988a705e2c554901b476734faede6a8f24d8b",
#					"directors_display":"Akira Kurosawa",
#					"description":"One of the most thrilling movie epics of all time, Seven Samurai (Shichinin no samurai) tells the story of a sixteenth-century village whose desperate inhabitants hire the eponymous war\u00adriors to protect them from invading bandits. This three-hour-plus ride from Akira Kurosawa\u2014featuring legendary actors Toshiro Mifune and Takashi Shimura\u2014seamlessly weaves philosophy and enter\u00adtainment, delicate human emotions and relentless action, into a rich, evocative, and unforgettable tale of courage and hope.",
#					"image":"https://s3.amazonaws.com/criterion-production/films/9d76c0395d994bbb7f9d6f7573796046/cThjxLEYjspgZR4c4TW7itGGxqawj8_original.jpg",
#					"url":"https://www.criterion.com/films/165-seven-samurai",
#					"published_at":"2024-12-06T11:51:27Z",
#					"meta":"1954, <i>Akira Kurosawa</i>",
#					"updated_at":"2024-11-14T23:00:49Z",
#					"director":"Akira Kurosawa",
#					"year":"1954",

# "original_title":"Le Mani sulla citt\u00e0",
#my $utf = '"original_title":"Le Mani sulla citt\u00e0"';
#print "UTF: '${utf}'\n";                    
#$utf =~ s/\\u(....)/ pack 'U*', hex($1) /eg;
#print "UTF: '${utf}'\n";                    


# Parse Criterion copy&paste order email format
sub parse_criterion
{
    my $file = shift;
    
    my $base_url = 'https://www.criterion.com';
    my $search_url = '/search';
    my $search_query_prefix = '?q=';
    my $search_json_pre =  'https://api.swiftype.com/api/v1/public/engines/search.json?callback=jQuery31007466145880065335_1733503549618&q=';
    my $search_json_post = '&engine_key=47z2a8Ae-x_co8MUSgoe&page=1&per_page=100&filters%5Bpage%5D%5Btype%5D%5B%5D=Film&filters%5Bpage%5D%5Btype%5D%5B%5D=Box%20Set&spelling=strict&_=1733503549619';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
    open ORDER, '<:encoding(UTF-8)', $file or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        if (/Items:/) {
            $b_in_items = 1;
        } elsif (/Subtotal:\s*\$(\d+\.\d+)/) {
            $b_in_items = 0;
        } elsif (/Order total:\s*\$(\d+\.\d+)/) {
            $total = $1;
            last;
        } elsif (/Each:\s*\$(\d+\.\d+)/) {
            if ($b_in_items) {
                $price = $1;
                
                my $search_query = uri_escape_utf8($title);
                my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
                # <div wire:snapshot="
                #    {&quot;data&quot;:
                #        {&quot;search&quot;:&quot;yuma&quot;,&quot;productsFound&quot;:1,&quot;contentPagesFound&quot;:2,&quot;products&quot;:
                #        }}}" wire:id="DH0AZmsy8K1FCqH8Eodk">
    
                my $search_query_json = $search_json_pre . $search_query . $search_json_post;
                
                my $found_url = '';
                #print "\nSearching: '${search_query_json}'\n";
                if (0 && open PAGE, "${wget} -q --referer='${base_url}/' -U '${useragent}' -O - '${search_query_json}' 2>/dev/null |") {
                    my $response_json = '';
                    while (<PAGE>) {
                        chomp;
                        $response_json = $response_json . $_;
                    }
                    close PAGE;
                    #print $response_json;
                    #print "\n\n";
                    
                    $response_json =~s/^\/\*\*\/jQuery.*?\(//;
                    $response_json =~s/\)$//;
                    my $jref = decode_json $response_json;
                    # HASH-ref of keys 'records'(HASH), 'info'(HASH), 'record_count'(SCALAR), 'errors'(HASH)
                    #for (keys %{$jref}) {
                    #    my $value = %{$jref}{$_};
                    #    print "key='$_' : value='$value'\n";
                    #}
                    #my $record_count = %{$jref}{record_count};
                    #print "Results: ${record_count}\n";
                    my $records = %{$jref}{records};
                    # HASH-ref of key 'page'(ARRAY)
                    my $page = %{$records}{page};
                    # ARRAY-ref
                    #print "Page: '$page' \n";
                    foreach my $record (@{$page}) {
                        # HASH-ref of SCALARS 'title', 'original_title', 'url', 'year', etc.
                        #print "Record ref: '$record'\n";
                        if (%{$record}{title} eq $title) {
                            #print "Original Title: '" . %{$record}{original_title} . "'\n";
                            #print "URL: '" . %{$record}{url} . "'\n";
                            #print "Year: '" . %{$record}{year} . "'\n";
                            my $original_title = %{$record}{original_title};
                            my $year = %{$record}{year};
                            $url = %{$record}{url};
                            if ($original_title && ($title ne $original_title)) {
                                $title = $title . ' / ' . $original_title;
                            }
                            if ($year) {
                                $title = $title . ' (' . $year . ')';
                            }
                            last;
                        }
                    }
                }
                elsif (open PAGE, "${wget} -q --referer='${base_url}/' -U '${useragent}' -O - '${search_query_url}' 2>/dev/null |") {
                    print "\nSearched: '${search_query_url}'\n";
                    my $response_json = '';
                    while (<PAGE>) {
                        #print;
                        if (/wire:snapshot="([^"]*)"/) {
                            $response_json = decode_entities($1);
                            last;
                        }
                    }
                    close PAGE;
                    #print "JSON: '$response_json'\n\n";
                    # {"data":{"search":"3:10 to Yuma","productsFound":1,"contentPagesFound":2,"products":[[[{"alternate_title":"3:10 to Yuma"
                    my $jref = decode_json $response_json;
                    #for (keys %{$jref}) {
                    #    my $value = %{$jref}{$_};
                    #    print "key='$_' : value='$value'\n";
                    #}
                    my $data = %{$jref}{data};
                    # HASH-ref of keys 'records'(HASH), 'info'(HASH), 'record_count'(SCALAR), 'errors'(HASH)
                    #for (keys %{$data}) {
                    #    my $value = %{$data}{$_};
                    #    print "data key='$_' : value='$value'\n";
                    #}
                    my $record_count = %{$data}{productsFound};
                    #print "Results: ${record_count}\n";
                    my $products = %{$data}{products};
                    foreach my $product (@{$products}) {
                        foreach my $arrs (@{$product}) {
                            foreach my $record (@{$arrs}) {
                                #print "Record ref: '$record'\n";
                                #for (keys %{$record}) {
                                #    my $value = %{$record}{$_};
                                #    print "record key='$_' : value='$value'\n";
                                #}
                                if (%{$record}{title} eq $title) {
                                    #print "Original Title: '" . %{$record}{original_title} . "'\n" if (%{$record}{original_title});
                                    #print "Slug: '" . %{$record}{slug} . "'\n";
                                    #print "Year: '" . %{$record}{year} . "'\n";
                                    my $original_title = %{$record}{original_title};
                                    my $year = %{$record}{year};
                                    my $slug = %{$record}{slug};
                                    $url = $base_url . '/' . $slug if ($slug);
                                    if ($original_title && ($title ne $original_title)) {
                                        $title = $title . ' / ' . $original_title;
                                    }
                                    if ($year) {
                                        $title = $title . ' (' . $year . ')';
                                    }
                                    last;
                                }
                                last if ($url);
                            }
                            last if ($url);
                        }
                        last if ($url);
                    }
                }
                
                my $spine = 0;
                if ($url ne '') {
                    print "--- URL: ${url}\n";
                    # Parse product page
                    if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                        while (<PAGE>) {
                            if (/<li data-duration-field>(\d+) minutes<\/li>/) {
                                $duration = $1;
                            }
                            if (/<li data-aspect-ratio-field>(\d\.\d+):1<\/li>/) {
                                $aspect = $1;
                            }
                            if (/<li><b>Spine \#(\d+)<\/b><\/li>/) {
                                $spine = $1;
                            }
                        }
                        close PAGE;
                    } else {
                        print "Failed to open $url\n";
                    }
                    if ($spine) {
                        print "Spine Number: ${spine}\n";
                    }
                    if ($aspect) {
                        print "Aspect Ratio: ${aspect}:1\n";
                    }
                    if ($duration) {
                        print "Duration: ${duration} min.\n";
                    }
                }
                
                # Incorporate extra data into return item elements
                $descr = 'criterion' . ($spine ? ' ' . $spine : '') . (($descr ne '') ? ' - ' . $descr : '');
                $aspect .= (($discs =~ /U/) ? 'ud' : (($discs =~ /D/) ? 'a' : 'h')) if ($aspect);
                $date = $eta unless ($date); # Expected delivery date
                
                my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
                push @items, $item;
                ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
                ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
            }
        } elsif ($b_in_items) {
            if (/Quantity: (\d+)/) {
                print "WARNING: Ordered ${1} copies!\n" if ($1 > 1);
            } elsif ($title eq '') {
                $title = $_;
                print "Title: '${title}'\n";
                ($duration, $aspect, $discs, $region, $lang, $sub) = (90, '1.85', '1B', 'NA', 'en(MA2.0)', 'en(SDH)');
            } elsif ($descr eq '') {
                $descr = $_ unless (/\s*\$\d+.\d+/);
                if ($descr =~s/4K UHD\+Blu-ray Combo//) {
                    $discs = '1U1B';
                } elsif ($descr =~s/4K Ultra HD//) {
                    $discs = '1U1B';
                } elsif ($descr =~s/Blu-ray//) {
                    $discs = '1B';
                } elsif ($descr =~s/DVD//) {
                    $discs = '1D';
                }
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Severin copy&paste order email format
sub parse_severin
{
    my $file = shift;
    
    my $base_url = 'https://severinfilms.com';
    my $search_url = '/search';
    my $search_query_prefix = '?q=';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        if (/Order summary/) {
            my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
               ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
            my $full_title = '';
            my ($sku, $brand, $format) = ('', '', '');
            while (<ORDER>) {
                chomp;
                next if /^\s*$/; # skip blank lines
                last if /Subtotal/;
                if ($title eq '') {
                    $full_title = '';
                    if (/^\s*(.*) × (\d)$/) {
                        $full_title = $1;
                        my $quantity = $2;
print "Title: '$full_title' in '${_}'\n";
                        if ($quantity > 1) {
                            print "\n\n    WARNING: Did you mean to order $quantity of $title?\n\n\n";
                        }
                    }
                    if ($full_title =~/^(.*) \[(.*)\]/) {
                        $title = $1;
                        $descr = $2;
                    } else {
                        $title = $full_title;
                        $descr = '';
                    }
                } elsif (/\$(\d+\.\d+)/) {
                    $price = $1;
                    
                    my $search_query = uri_escape($full_title);
                    my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
                    
                    print "\nSearching: '${search_query_url}'\n";
                    if (open PAGE, "${wget} -q -O - '${search_query_url}' 2>/dev/null |") {
                        while (<PAGE>) {
                            while (<PAGE>) {
                                if (/(\{"searchResult":\{.*\}\]\}\})\);\},/) {
                                    my $json = $1;
                                    #print "JSON: '${json}'\n\n";
                                    my $jref = decode_json $json;
                                    #for (keys %{$jref}) {
                                    #    my $value = %{$jref}{$_};
                                    #    print "key='$_' : value='$value'\n";
                                    #}
                                    if (%{$jref}{searchResult}) {
                                        my $resref = %{$jref}{searchResult};
                                        if (%{$resref}{productVariants}) {
                                            my $prodref = %{$resref}{productVariants};
                                            foreach my $variantref (@{$prodref}) {
                                                if (%{$variantref}{product}) {
                                                    my $productref = %{$variantref}{product};
                                                    if (%{$productref}{title} eq $full_title) {
                                                        $url = %{$productref}{url};
                                                        $url = $base_url . $url if ($url =~/^\//);
                                                        $url =~s/\?.*$//;
                                                        $brand = %{$productref}{vendor};
                                                        $sku = %{$variantref}{sku};
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        close PAGE;
                    }
                    
                    my $year = 0;
                    if ($url ne '') {
                        print "--- URL: ${url}\n";
                        # Parse product page
                        if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                            while (<PAGE>) {
                                if (/>Disc Specs:</) {
                                    while (<PAGE>) {
                                        if (/<li.*>Aspect ratio:\s*(.*?):1</i) {
                                            $aspect = $1;
                                        } elsif (/<li.*>Run ?time:\s*(\d+).*min.*</i) {
                                            $duration = $1;
                                        } elsif (/<li.*>Audio:\s*(.*?)</i) {
                                            $lang = $1;
                                        # <li>Audio: English Mono / Italian Mono</li>
                                        # <li>Closed Captions / English Subtitles</li>
                                        } elsif (/Subtitles/i) {
                                            $sub = 'SDH';
                                        } elsif (/Closed Captions/i) {
                                            $sub = 'CC';
                                        } elsif (/Region A/i) {
                                            $region = 'A';
                                        } elsif (/Region Free/i) {
                                            $region = 'Free';
                                        } elsif (/<li.*>4K Video: (.*?)</i) {
                                            $uhd = $1;
                                            if ($uhd =~/HDR10/) {
                                                $format = 'uh';
                                            } elsif ($uhd =~/Dolby Vision/) {
                                                $format = 'ud';
                                            } else {
                                                print;
                                            }
                                        } elsif (/<\/ul>/) {
                                            last;
                                        } else {
                                            print;
                                        }
                                    }
                                    last;
                                #} else {
                                #    print;
                                }
                            }
                            close PAGE;
                        } else {
                            print "Failed to open $url\n";
                        }
                        if ($duration) {
                            print "Duration: ${duration} min.\n";
                        }
                        if ($year) {
                            print "Year: ${year}\n";
                        }
                        if ($discs) {
                            print "Discs: ${discs}\n";
                        }
                        if ($region) {
                            print "Region: ${region}\n";
                        }
                        if ($lang) {
                            print "Language: ${lang}\n";
                        }
                        if ($aspect) {
                            print "Aspect Ratio: ${aspect}:1\n";
                        }
                        if ($format) {
                            print "Format: ${format}\n";
                        }
                    }
                    $discs = 1 unless ($discs);
                    if ($region eq 'Free') {
                        $region = 'N0';
                    } else {
                        $region = 'N' . $region;
                    }
                    if ($descr=~s/ Blu-ray//) {
                        $discs = $discs . 'B';
                    }
                    if ($descr=~s/ 4K UHD//) {
                        $discs = $discs . 'U';
                    }
                    if ($year) {
                        $title = $title . ' (' . $year . ')';
                    }
                    if ($lang =~/^(.*?)\s*Mono/) {
                        $lang = $1 . '(MA1.0)';
                    } elsif ($lang =~/^(.*?)\s*Stereo/) {
                        $lang = $1 . '(MA2.0)';
                    }
                    
                    $descr = 'Severin' . (($sku ne '') ? ' ' . $sku : '') . (($descr ne '') ? ' - ' . $descr : '');
                    $aspect .= (($format ne '') ? $format : 'h') if ($aspect);
                    $date = $eta unless ($date); # Expected delivery date
                    
                    my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
                    push @items, $item;
                    ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
                    ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
                }
            }
        } elsif (/Total/) {
            while (<ORDER>) {
                chomp;
                next if /^\s*$/; # skip blank lines
                if (/\$(\d+\.\d+)/) {
                    $total = $1;
                    last;
                }
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Vinegar Syndrome copy&paste order email format
sub parse_vinegar
{
    my $file = shift;
    
    # $ wget -q -O - 'https://vinegarsyndrome.com/search?type=product&q=Hundreds%20of%20Beavers' | grep -A 1 '<div class="product-info-inner">' | grep '<a href=' | grep -i '/Hundreds-of-Beavers?'
    # <a href="/products/hundreds-of-beavers?_pos=1&_sid=5f51d6626&_ss=r">
    
    my $base_url = 'https://vinegarsyndrome.com';
    my $search_url = '/search';
    my $search_query_prefix = '?type=product&q=';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        if (/Order Details:/) {
            $b_in_items = 1;
        } elsif (/Subtotal:/) {
            $b_in_items = 0;
        } elsif (/Total:\s*(\d+\.\d+)/) {
            if ($b_in_items) {
                $price = $1;
                
                my $search_query = uri_escape($title);
                my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
                
                my $found_url = '';
                #print "\nSearching: '${search_query_url}'\n";
                if (open PAGE, "${wget} -q -O - '${search_query_url}' 2>/dev/null |") {
                    while (<PAGE>) {
                        next unless (/<div class="product-info-inner">/);
#  <div class="product-info-inner">
#    <a href="/products/hundreds-of-beavers?_pos=1&_sid=e391d987d&_ss=r">
#      <span class="prod-vendor">Cartuna</span>
#      <span class="prod-title">Hundreds of Beavers</span>
#      
#    </a>
                        while (<PAGE>) {
                            if (/<a href="(.*)\?_pos=/) {
                                $found_url = $1;
                            } elsif (/<span class="prod-title">(.*)<\/span>/) {
                                my $found_title = $1;
                                if ($found_title eq $title) {
                                    $url = $found_url;
                                    $url = $base_url . $url if ($url=~/^\//);
                                }
                            } elsif (/<\/a>/) {
                                last;
                            }
                        }
                    }
                    close PAGE;
                }
                
                my ($sku, $brand, $year, $format, $hdr) = ('', '', 0, '', '');
                if ($url ne '') {
#data-sheets-root="1">Regina Lei, Berant Zhu, Tzu-Chiang Wang, Apple Chen, Wei-Hua Lan, Ralf Chiu<br>2021 / 99 min / 2.00:1 / </span></span>Mandarin Chinese, Hokkien DTS-HD MA 5.1</p>

#div
#    class="yotpo-widget-instance"
#    data-yotpo-instance-id="457238"
#    data-yotpo-product-id="7334868811818"
#    data-yotpo-name="The Sadness"
#    data-yotpo-url="https://vinegarsyndrome.com/products/the-sadness"
#    data-yotpo-price="26.99"
#    data-yotpo-currency="USD"
#    data-yotpo-image-url="https://vinegarsyndrome.com/cdn/shop/files/thesadnessboxfront.jpg?v=1719423605&width=480"
#    data-yotpo-description="2024 Subscribers: This is NOT included in your Subscription. If you&#39;d like to purchase it, you will need to login to view your special 50% off SRP pricing.
#This listing is for the standard edition 4K UHD/Blu-ray set. The limited edition slipcase + slipcover combo (designed by Chris Barnes) was limited to 2,000 units and is sold out. The two versions are identical, aside from the packaging.
#Called “one of the best streaming services in the world” by RogerEbert.com and described by Thrillist as “pretty much everything a horror fan could want,” Shudder is a premium streaming service offering the best selection of horror, thriller and supernatural movies, series and specials.
#
#After a year of combating a pandemic with relatively benign symptoms, a frustrated nation finally lets its guard down. This is when the virus spontaneously mutates, giving rise to a mind-altering plague. The streets erupt into violence and depravity, as those infected are driven to enact the most cruel and ghastly things they can think of. Murder, torture, rape and mutilation are only the beginning. A young couple is pushed to the limits of sanity as they try to reunite amid the chaos. The age of civility and order is no more. There is only &quot;The Sadness&quot;.
#directed by: Rob Jabbazstarring: Regina Lei, Berant Zhu, Tzu-Chiang Wang, Apple Chen, Wei-Hua Lan, Ralf Chiu2021 / 99 min / 2.00:1 / Mandarin Chinese, Hokkien DTS-HD MA 5.1
#Additional info: 
#
#
#Region Free 4K UHD / Region A Blu-ray
#New feature audio commentary with director Rob Jabbaz and composers Tzechar
#New feature audio commentary with film critic Simon Abrams 
#Feature audio commentary with director Rob Jabbaz and medical advisor Shu 
#Four previously released featurettes: SFX, Director, Art Director, BusinessMan 
#New featurette: The Production 
#New video essay by Samm Deighan 
#Two exclusive short films: Clearwater and Fiendish Funnies, both from director Rob Jabbaz 
#Numerous theatrical trailers 
#Booklet featuring new writing by film critic Brandon Streussnig as well as exclusive storyboards and a &quot;gore guide&quot; 
#English subtitles
#
                    print "--- URL: ${url}\n";
                    # parse product page
                    if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                        while (<PAGE>) {
                            my $json = '';
                            if (/<script\stype=\"application\/ld\+json\">/) {
                                while (<PAGE>) {
                                    last if (/<\/script>/);
                                    $json .= $_;
                                }
                                #print "JSON: '${json}'\n\n";
                                my $jref = decode_json $json;
                                # HASH-ref of keys 'offers'(ARRAY), 'sku'(SCALAR), 'brand'(SCALAR), 'description'(SCALAR)
                                for (keys %{$jref}) {
                                    my $value = %{$jref}{$_};
                                    #print "key='$_' : value='$value'\n";
                                }
                                $sku = %{$jref}{sku} if (%{$jref}{sku});
                                $title = %{$jref}{name} if (%{$jref}{name});
                                $title =~s/&#(\d+);/chr($1)/ge;
                                $brand = %{$jref}{brand} if (%{$jref}{brand});
                                if (%{$jref}{description}) {
                                    my $description = %{$jref}{description};
                                    #print "DESCRIPTION: '${description}'\n\n";
                                    for (split (/\n/, $description)) {
                                        # 1998 / 78 min / 1.33:1 / English DTS-HD MA 2.0
                                        # 2010 / 107 min / 2.35:1 / Spanish DTS-HD MA 5.1
                                        # 1994 / 102 min / 1.85:1 / English DTS-HD MA 5.1, 2.0
                                        # 2022 / 124 min / 2.00:1 / Mongolian DTS-HD MA 2.0
                                        # 2018 / 93 min / 1.85:1 / Swedish DTS-HD MA 5.1
                                        # 1974 / 83 min / 1.85:1 / English DTS-HD Master Audio Mono
                                        # 1986 / 174 min (combined) / 1.85:1 / Cantonese, Mandarin, English DTS-HD Master Audio Mono
                                        #   The Seventh Curse (Hong Kong Version): 1986 / 84 min / 1.85:1 / Cantonese Mono
                                        #   The Seventh Curse (English Version): 1986 / 81 min / 1.85:1 / English Mono
                                        #   Witch From Nepal: 1986 / 90 min / 1.85:1 / Cantonese & Mandarin Mono
                                        # 1982-1984 / 191 minutes (combined) / Color / 1.85:1
                                        # 1973 / 96 min / 1.85:1 / English DTS-HD MA 1.0
                                        # 1995 / 106 min / 1.85:1 / English DTS-HD Master Audio 5.1
                                        # 1982 / 92 min / 1.85:1 / English Mono
                                        # 68 minutes / Color / AR: 1.85:1
                                        # 1983 / 96 min / 2.35:1 / English DTS-HD Master Audio Stereo
                                        # 1987 / 94 min / 1.85:1 / Italian 2.0 Stereo + English dub
                                        if (/(\d{4}[-\d]*) \/ (\d+)\s?min.*? \/.* (\d.\d{2}):1/) {
                                            print "INFO: '${_}'\n";
                                            $year = $1;
                                            $duration = $2;
                                            $aspect = $3;
                                            my ($codec, $channels) = ('', '');
                                            if (/English/) {
                                                $lang = 'en';
                                            }
                                            if ((/DTS-HD MA/) || (/DTS-HD Master Audio/)) {
                                                $codec = 'MA';
                                            }
                                            if (/Mono/) {
                                                $channels = '1.0';
                                            } elsif (/Stereo/) {
                                                $channels = '2.0';
                                            } elsif (/\s(\d\.[01])/) {
                                                $channels = $1;
                                            }
                                            if (($codec ne '') || ($channels ne '')) {
                                                $lang .= '(';
                                                $lang .= $codec if ($codec ne '');
                                                $lang .= $channels if ($channels ne '');
                                                $lang .= ')';
                                            }
                                        }
                                        # 2-disc Region A Blu-ray Set
                                        # 2-disc Set: 4K Ultra HD / Region A Blu-ray
                                        # Region Free Blu-ray
                                        # Region Free Blu-ray
                                        # 4K Ultra HD / Region Free Blu-ray Set
                                        # Region free Blu-ray/DVD combo pack
                                        # Region free Blu-ray and DVD combo pack
                                        # 2-disc Set: 4K Ultra HD (High Bitrate UHD100) / Region A Blu-ray
                                        #   4K UHD presented in Dolby Vision High-Dynamic-Range
                                        # Dual Layer DVD-9 | Region Free | 16:9 Anamorphic | 1.85:1 AR | MONO
                                        # Region Free UHD / Region A Blu-ray
                                        # 4-disc Set: 4K Ultra HD (UHD66) x2 / Region A Blu-ray x2
                                        if (/Region/) {
                                            if (/(\d+)-disc Set/) {
                                                $discs = $1;
                                            } else {
                                                $discs = '1';
                                            }
                                            if (/4K Ultra HD/) {
                                                $format = 'u';
                                                $discs .= 'U';
                                            } elsif (/Blu-ray/) {
                                                $format = 'h';
                                                $discs .= 'B';
                                            }
                                            if (/Region A Blu-ray/) {
                                                $region = 'A';
                                                if ($discs eq '2U') {
                                                    $discs = '1U1B';
                                                }
                                            } elsif (/Region free Blu-ray/i) {
                                                $region = 'Free';
                                                if ($discs eq '2U') {
                                                    $discs = '1U1B';
                                                }
                                            }
                                        }
                                        # 4K UHD presented in Dolby Vision High-Dynamic-Range
                                        # 4K UHDs presented in DolbyVision HDR
                                        # 4K UHD presented in High-Dynamic-Range
                                        if (/presented in/ && ($format eq 'u')) {
                                            if (/Dolby\s*Vision/) {
                                                $hdr = 'd';
                                            } elsif (/High-Dynamic-Range/) {
                                                $hdr = 'h';
                                            }
                                        }
                                        # English SDH subtitles
                                        # English subtitles
                                        if (/English SDH subtitles/) {
                                            $sub = 'en(SDH)';
                                        } elsif (/English subtitles/) {
                                            $sub = 'en';
                                        }
                                    }
                                }
                            }
                        }
                        close PAGE;
                    } else {
                        print "Failed to open $url\n";
                    }
                    if ($sku) {
                        print "SKU: ${sku}\n";
                    }
                    if ($year) {
                        print "Year: ${year}\n";
                    }
                    if ($duration) {
                        print "Duration: ${duration} min.\n";
                    }
                    if ($discs) {
                        print "Discs: ${discs}\n";
                    }
                    if ($region) {
                        print "Region: ${region}\n";
                    }
                    if ($hdr) {
                        print "HDR: ${hdr}\n";
                    }
                    if ($lang) {
                        print "Language: ${lang}\n";
                    }
                    if ($sub) {
                        print "Subtitles: ${sub}\n";
                    }
                    if ($aspect) {
                        print "Aspect Ratio: ${aspect}:1\n";
                    }
                    if ($format) {
                        print "Format: ${format}\n";
                    }
                }
                
                $discs = 1 unless ($discs);
                if ($region eq 'Free') {
                    $region = 'N0';
                } else {
                    $region = 'N' . $region;
                }
                if ($year) {
                    $title = $title . ' (' . $year . ')';
                }
                if ($aspect) {
                    $aspect .= (($format ne '') ? $format : 'h') . $hdr;
                }
                if ($brand eq 'Vinegar Syndrome') {
                    $brand = '';
                }
                $descr = 'Vinegar Syndrome' . (($sku ne '') ? ' ' . $sku : '') . (($brand ne '') ? ' - ' . $brand : '') . (($descr ne '') ? ' - ' . $descr : '');
                $date = $eta unless ($date); # Expected delivery date
                
                my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
                push @items, $item;
                ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
                ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
            } else {
                $total = $1;
                last;
            }
        } elsif ($b_in_items) {
            if ($title eq '') {
                if (/^\s*(.*)\s*$/) {
                    $title = $1;
                } else {
                    $title = $_;
                }
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Mélusine copy&paste order email format
sub parse_melusine
{
    my $file = shift;
    
    my $base_url = 'https://melusine.com';
    my $search_url = '/search';
    my $search_query_prefix = '?type=product&q=';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my $b_in_sku = 0;
    my $sku = '';
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        #print "LINE: '${_}'\n";
        if ($b_in_sku) {
            $sku = $_;
            $b_in_sku = 0;
            print "SKU from order: ${sku}\n";
            
            my $search_query = uri_escape($sku);
            my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
            
            my $found_url = '';
            print "\nSearching: '${search_query_url}'\n";
            if (open PAGE, "${wget} -q -O - '${search_query_url}' 2>/dev/null |") {
                while (<PAGE>) {
                    next unless (/<div class="product-info-inner">/);
                    while (<PAGE>) {
                        if (/<a href="(.*)\?_pos=/) {
                            $found_url = $1;
                        } elsif (/<span class="prod-title">(.*)<\/span>/) {
                            my $found_title = $1;
                            if ($found_title eq $title) {
                                $url = $found_url;
                                $url = $base_url . $url if ($url=~/^\//);
                            }
                        } elsif (/<\/a>/) {
                            last;
                        }
                    }
                }
                close PAGE;
            }
            
            my ($brand, $year, $format, $hdr) = ('', 0, '', '');
            if ($url ne '') {
                print "--- URL: ${url}\n";
                # parse product page
                if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                    while (<PAGE>) {
                        my $json = '';
                        if (/<script\stype=\"application\/ld\+json\">/) {
                            while (<PAGE>) {
                                last if (/<\/script>/);
                                $json .= $_;
                            }
                            #print "JSON: '${json}'\n\n";
                            my $jref = decode_json $json;
                            # HASH-ref of keys 'offers'(ARRAY), 'sku'(SCALAR), 'brand'(SCALAR), 'description'(SCALAR), 'name'(SCALAR)
                            for (keys %{$jref}) {
                                my $value = %{$jref}{$_};
                                print "key='$_' : value='$value'\n";
                            }
                            unless ($sku) {
                                $sku = %{$jref}{sku} if (%{$jref}{sku});
                            }
                            $title = %{$jref}{name} if (%{$jref}{name});
                            $title =~s/&#(\d+);/chr($1)/ge;
                            $brand = %{$jref}{brand} if (%{$jref}{brand});
                            if (%{$jref}{description}) {
                                my $description = %{$jref}{description};
                                #print "DESCRIPTION: '${description}'\n\n";
                                for (split (/\n/, $description)) {
                                    # 1998 / 78 min / 1.33:1 / English DTS-HD MA 2.0
                                    # 2010 / 107 min / 2.35:1 / Spanish DTS-HD MA 5.1
                                    # 1994 / 102 min / 1.85:1 / English DTS-HD MA 5.1, 2.0
                                    # 2022 / 124 min / 2.00:1 / Mongolian DTS-HD MA 2.0
                                    # 2018 / 93 min / 1.85:1 / Swedish DTS-HD MA 5.1
                                    # 1974 / 83 min / 1.85:1 / English DTS-HD Master Audio Mono
                                    # 1986 / 174 min (combined) / 1.85:1 / Cantonese, Mandarin, English DTS-HD Master Audio Mono
                                    #   The Seventh Curse (Hong Kong Version): 1986 / 84 min / 1.85:1 / Cantonese Mono
                                    #   The Seventh Curse (English Version): 1986 / 81 min / 1.85:1 / English Mono
                                    #   Witch From Nepal: 1986 / 90 min / 1.85:1 / Cantonese & Mandarin Mono
                                    # 1982-1984 / 191 minutes (combined) / Color / 1.85:1
                                    # 1973 / 96 min / 1.85:1 / English DTS-HD MA 1.0
                                    # 1995 / 106 min / 1.85:1 / English DTS-HD Master Audio 5.1
                                    # 1982 / 92 min / 1.85:1 / English Mono
                                    # 68 minutes / Color / AR: 1.85:1
                                    # 1983 / 96 min / 2.35:1 / English DTS-HD Master Audio Stereo
                                    # 1987 / 94 min / 1.85:1 / Italian 2.0 Stereo + English dub
                                    # 1972-75 / 208 minutes (combined) / Color / 1.85:1
                                    if (/(\d{4}[-\d]*) \/ (\d+)\s?min.*? \/.* (\d.\d{2}):1/) {
                                        print "INFO: '${_}'\n";
                                        $year = $1;
                                        $duration = $2;
                                        $aspect = $3;
                                        my ($codec, $channels) = ('', '');
                                        if (/English/) {
                                            $lang = 'en';
                                        }
                                        if ((/DTS-HD MA/) || (/DTS-HD Master Audio/)) {
                                            $codec = 'MA';
                                        }
                                        if (/Mono/) {
                                            $channels = '1.0';
                                        } elsif (/Stereo/) {
                                            $channels = '2.0';
                                        } elsif (/\s(\d\.[01])/) {
                                            $channels = $1;
                                        }
                                        if (($codec ne '') || ($channels ne '')) {
                                            $lang .= '(';
                                            $lang .= $codec if ($codec ne '');
                                            $lang .= $channels if ($channels ne '');
                                            $lang .= ')';
                                        }
                                    }
                                    # 2-disc Region A Blu-ray Set
                                    # 2-disc Set: 4K Ultra HD / Region A Blu-ray
                                    # Region Free Blu-ray
                                    # Region Free Blu-ray
                                    # 4K Ultra HD / Region Free Blu-ray Set
                                    # Region free Blu-ray/DVD combo pack
                                    # Region free Blu-ray and DVD combo pack
                                    # 2-disc Set: 4K Ultra HD (High Bitrate UHD100) / Region A Blu-ray
                                    #   4K UHD presented in Dolby Vision High-Dynamic-Range
                                    # Dual Layer DVD-9 | Region Free | 16:9 Anamorphic | 1.85:1 AR | MONO
                                    # Region Free UHD / Region A Blu-ray
                                    # 4-disc Set: 4K Ultra HD (UHD66) x2 / Region A Blu-ray x2
                                    if (/Region/) {
                                        if (/(\d+)-disc Set/) {
                                            $discs = $1;
                                        } else {
                                            $discs = '1';
                                        }
                                        if (/4K Ultra HD/) {
                                            $format = 'u';
                                            $discs .= 'U';
                                        } elsif (/Blu-ray/) {
                                            $format = 'h';
                                            $discs .= 'B';
                                        } elsif (/Blu-ray/) {
                                            $format = 'a';
                                            $discs .= 'D';
                                        }
                                        if (/Region A Blu-ray/) {
                                            $region = 'A';
                                            if ($discs eq '2U') {
                                                $discs = '1U1B';
                                            }
                                        } elsif (/Region free Blu-ray/i) {
                                            $region = 'Free';
                                            if ($discs eq '2U') {
                                                $discs = '1U1B';
                                            }
                                        } elsif (/Region 1 DVD/) {
                                            $region = '1';
                                        } elsif (/Region free DVD/i) {
                                            $region = 'Free';
                                        }
                                    }
                                    # 4K UHD presented in Dolby Vision High-Dynamic-Range
                                    # 4K UHDs presented in DolbyVision HDR
                                    # 4K UHD presented in High-Dynamic-Range
                                    if (/presented in/ && ($format eq 'u')) {
                                        if (/Dolby\s*Vision/) {
                                            $hdr = 'd';
                                        } elsif (/High-Dynamic-Range/) {
                                            $hdr = 'h';
                                        }
                                    }
                                    # English SDH subtitles
                                    # English subtitles
                                    if (/English SDH subtitles/) {
                                        $sub = 'en(SDH)';
                                    } elsif (/English subtitles/) {
                                        $sub = 'en';
                                    }
                                }
                            }
                        }
                    }
                    close PAGE;
                } else {
                    print "Failed to open $url\n";
                }
                if ($sku) {
                    print "SKU: ${sku}\n";
                }
                if ($year) {
                    print "Year: ${year}\n";
                }
                if ($duration) {
                    print "Duration: ${duration} min.\n";
                }
                if ($discs) {
                    print "Discs: ${discs}\n";
                }
                if ($region) {
                    print "Region: ${region}\n";
                }
                if ($hdr) {
                    print "HDR: ${hdr}\n";
                }
                if ($lang) {
                    print "Language: ${lang}\n";
                }
                if ($sub) {
                    print "Subtitles: ${sub}\n";
                }
                if ($aspect) {
                    print "Aspect Ratio: ${aspect}:1\n";
                }
                if ($format) {
                    print "Format: ${format}\n";
                }
            }
            
            $discs = 1 unless ($discs);
            if ($region eq 'Free') {
                $region = 'N0';
            } else {
                $region = 'N' . $region;
            }
            if ($year) {
                $title = $title . ' (' . $year . ')';
            }
            if ($aspect) {
                $aspect .= (($format ne '') ? $format : 'h') . $hdr;
            }
            if ($brand eq 'Vinegar Syndrome') {
                $brand = '';
            }
            $descr = 'M' . chr(0x00e9) . 'lusine / Vinegar Syndrome' . (($sku ne '') ? ' ' . $sku : '') . (($brand ne '') ? ' - ' . $brand : '') . (($descr ne '') ? ' - ' . $descr : '');
            $date = $eta unless ($date); # Expected delivery date
            
            my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
            push @items, $item;
            ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
            ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
        } elsif (/Product SKU:/) {
            $b_in_sku = 1;
        } elsif (/Order Summary/) {
            $b_in_items = 1;
        } elsif (/Total price:\s+\$(\d+\.\d+)/) {
            $b_in_items = 0;
            $total = $1;
            last;
        } elsif (/^(.*) × (\d)$/) {
            if ($b_in_items) {
                $title = $1;
                my $quantity = $2;
                if ($quantity > 1) {
                    print "\n\n    WARNING: Did you mean to order $quantity of $title?\n\n\n";
                }
            }
        } elsif (/\$(\d+\.\d+)/) {
            if ($b_in_items) {
                $price = $1;
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Arrow copy&paste order email format
sub parse_arrow
{
    my $file = shift;
    
    my $base_url = 'https://www.arrowvideo.com';
    my $search_url = '/search/';
    my $search_query_prefix = '?q=';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        if (/Order Details/) {
            $b_in_items = 1;
        } elsif (/Order Summary/) {
            $b_in_items = 0;
        } elsif (/Price\s*\$(\d+\.\d+)/) {
            if ($b_in_items) {
                $price = $1;
                
                my $search_title = $title;
                $search_title =~s/\s*\|.*$//;
                my $search_query = uri_escape($search_title);
                my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
                
                my ($brand, $sku) = ('', 0);
                #print "\nSearching: '${search_query_url}'\n";
                if (open PAGE, "${wget} -q -O - '${search_query_url}' 2>/dev/null |") {
                    while (<PAGE>) {
                        if (/const biData = ({"query":.*});/) {
                            my $json = $1;
                            #print "JSON: '${json}'\n\n";
                            my $jref = decode_json $json;
                            #for (keys %{$jref}) {
                            #    my $value = %{$jref}{$_};
                            #    print "key='$_' : value='$value'\n";
                            #}
                            if (%{$jref}{products}) {
                                my $prodref = %{$jref}{products};
                                foreach my $productref (@{$prodref}) {
                                    if (%{$productref}{title} eq $title) {
                                        $url = %{$productref}{url};
                                        $url = $base_url . $url if ($url =~/^\//);
                                        $url =~s/\?.*$//;
                                        my $brandref = %{$productref}{brand};
                                        $brand = %{$brandref}{name};
                                        $sku = %{$productref}{sku};
                                    } else {
                                        print "Title miss: '" . %{$productref}{title} . "' != '$title'\n";
                                    }
                                }
                            }
                        }
                    }
                    close PAGE;
                }
                
                my $year = 0;
                if ($url ne '') {
                    print "--- URL: ${url}\n";
                    # Parse product page
                    if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                        while (<PAGE>) {
                            if (/<div[^>]*>Run Time:<\/div>/ && (! $duration)) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(.*)<\/div>/) {
                                        my $runs = $1;
                                        print "Durations: '${runs}'\n";
                                        for (split (/ \/ /, $runs)) {
                                            if (/(\d+)/) {
                                                $duration = ($duration ? $duration . '/' : '') . $1;
                                            }
                                        }
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Theatrical Release Year:<\/div>/) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(\d+)<\/div>/) {
                                        $year = $1;
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Number of Discs:<\/div>/) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(\d+)<\/div>/) {
                                        $discs = $1;
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Region:<\/div>/) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(.+)<\/div>/) {
                                        $region = $1;
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Main Language:<\/div>/) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(.+)<\/div>/) {
                                        $lang = $1;
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Subtitle Languages:<\/div>/) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(.+)<\/div>/) {
                                        $sub = $1;
                                        last;
                                    }
                                }
                            } elsif (/<div[^>]*>Aspect Ratio:<\/div>/ && (! $aspect)) {
                                while (<PAGE>) {
                                    if (/<div[^>]*>(.*)<\/div>/) {
                                        my $ars = $1;
                                        print "ARs: '${ars}'\n";
                                        for (split (/ \/ /, $ars)) {
                                            if (/(\d\.\d+):1/) {
                                                $aspect = ($aspect ? $aspect . '/' : '') . $1;
                                            }
                                        }
                                        last;
                                    }
                                }
                            }
                        }
                        close PAGE;
                    } else {
                        print "Failed to open $url\n";
                    }
                    if ($duration) {
                        print "Duration: ${duration} min.\n";
                    }
                    if ($year) {
                        print "Year: ${year}\n";
                    }
                    if ($discs) {
                        print "Discs: ${discs}\n";
                    }
                    if ($region) {
                        print "Region: ${region}\n";
                    }
                    if ($lang) {
                        print "Language: ${lang}\n";
                    }
                    if ($sub) {
                        print "Subtitles: ${sub}\n";
                    }
                    if ($aspect) {
                        print "Aspect Ratio: ${aspect}:1\n";
                    }
                }
                $discs = 1 unless ($discs);
                if ($region eq 'Free') {
                    $region = 'N0';
                } else {
                    $region = 'N' . $region;
                }
                if ($title=~s/ Blu-ray//) {
                    $discs = $discs . 'B';
                }
                if ($title=~s/ 4K UHD//) {
                    $discs = $discs . 'U';
                }
                if ($title=~s/ Limited Edition//) {
                    $descr = ($descr ? $descr . ' '  : '') . 'Limited Edition';
                }
                if ($year) {
                    $title = $title . ' (' . $year . ')';
                }
                $lang =~s/<p>([^<]*)<\/p>/\/$1/g;
                $lang =~s/English/en/g;
                $lang =~s/\s*\/\s*/\//g;
                $sub =~s/<p>([^<]*)<\/p>/\/$1/g;
                $sub =~s/English/en/g;
                $sub =~s/ (SDH)/($1)/;
                $sub =~s/\s*\/\s*/\//g;
                if ($brand eq 'Arrow Video') {
                    $brand = '';
                }
                $descr = 'Arrow' . (($descr ne '') ? ' - ' . $descr : '' . (($brand ne '') ? ' - ' . $brand : ''));
                $aspect .= 'h' if ($aspect);
                $date = $eta unless ($date); # Expected delivery date
                
                my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
                push @items, $item;
                ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
                ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
            }
        } elsif (/Total Cost:\s*\$(\d+\.\d+)/) {
            $total = $1;
            last;
        } elsif ($b_in_items) {
            if ($title eq '') {
                if (/^\s*(.*)\s*$/) {
                    $title = $1;
                } else {
                    $title = $_;
                }
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Sentai Film Works copy&paste order email format
sub parse_sentai
{
    my $file = shift;
    
    my $base_url = 'https://www.sentaifilmworks.com';
    my $search_url = '/search';
    my $search_query_prefix = '?q=';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my $b_in_total = 0;
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url, $sku) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  , ''  );
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        #print "LINE: '${_}'\n";
        if (/Order summary/) {
            $b_in_items = 1;
        } elsif (/^Total$/) {
            $b_in_total = 1;
            $b_in_items = 0;
        } elsif (/^(.*) × (\d)$/) {
            if ($b_in_items) {
                $title = $1;
                #print "Title: '${title}'\n";
                my $quantity = $2;
                $title =~s/\t//;
                if ($quantity > 1) {
                    print "\n\n    WARNING: Did you mean to order $quantity of $title?\n\n\n";
                }
            }
        } elsif (/\$(\d+\.\d+)/) {
            if ($b_in_items) {
                $price = $1;
                #print "Price: '${price}'\n";
            } elsif ($b_in_total) {
                $total = $1;
                $b_in_total = 0;
                last;
            } else {
                next;
            }
            
            if ($title ne '') {
                my $search_query = uri_escape($title);
                my $search_query_url = $base_url . $search_url . $search_query_prefix . $search_query;
                
                print "\nSearching: '${search_query_url}'\n";
                if (open PAGE, "${wget} -q -O - '${search_query_url}' 2>/dev/null |") {
                    while (<PAGE>) {
                        while (<PAGE>) {
                            if (/<script\stype=\"application\/ld\+json\">/) {
                                my $json = '';
                                while (<PAGE>) {
                                    last if (/<\/script>/);
                                    $json .= $_;
                                }
                                #print "JSON: '${json}'\n\n";
                                my $jref = decode_json $json;
                                # HASH-ref of keys 'offers'(ARRAY), 'sku'(SCALAR), 'brand'(SCALAR), 'description'(SCALAR), 'name'(SCALAR)
                                #for (keys %{$jref}) {
                                #    my $value = %{$jref}{$_};
                                #    print "key='$_' : value='$value'\n";
                                #}
                                $sku = %{$jref}{sku} if (%{$jref}{sku});
                                #my $found_title = '';
                                #$title = %{$jref}{name} if (%{$jref}{name});
                                #if ($found_title ne $title) {
                                #    next;
                                #}
                                if (%{$jref}{offers}) {
                                    my $offers = %{$jref}{offers};
                                    # ARRAY-ref
                                    #print "Page: '$page' \n";
                                    foreach my $offer (@{$offers}) {
                                        # HASH-ref of SCALARS 'url', etc.
                                        $url = %{$offer}{url};
                                        last;
                                    }
                                }
                            }
                        }
                    }
                    close PAGE;
                }
                
                my ($brand, $year, $format, $hdr) = ('', 0, '', '');
                if ($url ne '') {
                    print "--- URL: ${url}\n";
                    # parse product page
                    if (open PAGE, "${wget} -q -U '${useragent}' -O - '${url}' 2>/dev/null |") {
                        while (<PAGE>) {
                            next unless (/<ul class="list-unstyled prod-details">/);
                            while (<PAGE>) {
                                if (/<strong>Runtime:<\/strong>\s+<span class="runtime">(\d+) min.<\/span>/) {
                                    $duration = $1;
                                }
                            }
                        }
                        close PAGE;
                    } else {
                        print "Failed to open $url\n";
                    }
                    if ($sku) {
                        print "SKU: ${sku}\n";
                    }
                    if ($year) {
                        print "Year: ${year}\n";
                    }
                    if ($duration) {
                        print "Duration: ${duration} min.\n";
                    }
                    if ($discs) {
                        print "Discs: ${discs}\n";
                    }
                    if ($region) {
                        print "Region: ${region}\n";
                    }
                    if ($hdr) {
                        print "HDR: ${hdr}\n";
                    }
                    if ($lang) {
                        print "Language: ${lang}\n";
                    }
                    if ($sub) {
                        print "Subtitles: ${sub}\n";
                    }
                    if ($aspect) {
                        print "Aspect Ratio: ${aspect}:1\n";
                    }
                    if ($format) {
                        print "Format: ${format}\n";
                    }
                }
                
                $discs = '1B' unless ($discs);
                $region = 'A' unless ($region);
                $lang = 'en(MA2.0)/ja(MA2.0)' unless ($lang);
                $sub = 'en' unless ($sub);
                $aspect = '1.78' unless ($aspect);
                $format = 'h' unless ($format);
                if ($region eq 'Free') {
                    $region = 'N0';
                } else {
                    $region = 'N' . $region;
                }
                if ($year) {
                    $title = $title . ' (' . $year . ')';
                }
                if ($aspect) {
                    $aspect .= (($format ne '') ? $format : 'h') . $hdr;
                }
                $descr = 'Sentai' . (($sku ne '') ? ' ' . $sku : '') . (($descr ne '') ? ' - ' . $descr : '');
                $date = $eta unless ($date); # Expected delivery date
                
                my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
                push @items, $item;
                ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url, $sku) = 
                ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  , ''  );
            }
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Parse Kino Lorber copy&paste order email format
sub parse_kino
{
    my $file = shift;
    
    my $base_url = 'https://kinolorber.com';
    my $search_json_pre =  'https://api.swiftype.com/api/v1/public/engines/search.json?callback=jQuery31007466145880065335_1733503549618&q=';
    my $search_json_post = '&engine_key=47z2a8Ae-x_co8MUSgoe&page=1&per_page=100&filters%5Bpage%5D%5Btype%5D%5B%5D=Film&filters%5Bpage%5D%5Btype%5D%5B%5D=Box%20Set&spelling=strict&_=1733503549619';
    
    my $total = 0.00;
    my @items = (); # Array of items
    
    my $b_in_items = 0;
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
       ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
    open ORDER, '<:encoding(UTF-8)', $file or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        if (/^QTY/) {
            $b_in_items = 1;
        } elsif (/^Sub-Total\s*\$(\d+\.\d+)/) {
            $b_in_items = 0;
        } elsif (/^Total\s*\$(\d+\.\d+)/) {
            $total = $1;
            last;
        } elsif ((/^(\d+) \t(.*) \((.*)\)/) && $b_in_items) {
            $title = $2;
            $descr = $3;
            my $quantity = $1;
            if ($quantity > 1) {
                print "\n\n    WARNING: Did you mean to order $quantity of $title?\n\n\n";
            }
        } elsif ((/^\t\$(\d+\.\d+)/) && $b_in_items) {
            $price = $1;
            
            # https://kinolorber.com/search?q=The+Man+Who+Was+Sherlock+Holmes
            my $search_query = uri_escape_utf8($title);
            
            # Incorporate extra data into return item elements
            $descr = 'Kino Lorber' . (($descr ne '') ? ' - ' . $descr : '');
            $date = $eta unless ($date); # Expected delivery date
            
            my $item = "${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t${price}\t${asin}\t${url}";
            push @items, $item;
            ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = 
            ('',     '',     0,         '',      '',     '',      '',    '',   '',    '',    0,      '',    ''  );
        }
    }
    close ORDER;
    
    return ($total, @items);
}

# Shipment variables
my $total = 0.00;
my @items = (); # Array of items for entire shipment (all given orders, with combined shipment charge)

# Input: plain text of Order E-Mail
while (my $file = shift) 
{

    # Determine vendor
    my $label = 0;
    open ORDER, "<${file}" or die "Order file '${file}' not found!";
    while (<ORDER>) {
        chomp;
        next if /^\s*$/; # skip blank lines
        $label = 1 if /Criterion Collection/;
        $label = 2 if /Severin Films/;
        $label = 3 if /Vinegar Syndrome 100 Congress Street Bridgeport/;
        $label = 4 if /Mélusine/;
        $label = 5 if /Arrow Films/;
        $label = 6 if /Sentai\s+Order/;
        $label = 7 if /Kino Lorber/;
        last if $label;
    }
    close ORDER;

    # Order variables
    my $o_total = 0.00;
    my @o_items = (); # Array of items

    # Extract items from order (vendor specific)
    if ($label == 1) {
        ($o_total, @o_items) = parse_criterion($file);
    } elsif ($label == 2) {
        ($o_total, @o_items) = parse_severin($file);
    } elsif ($label == 3) {
        ($o_total, @o_items) = parse_vinegar($file);
    } elsif ($label == 4) {
        ($o_total, @o_items) = parse_melusine($file);
    } elsif ($label == 5) {
        ($o_total, @o_items) = parse_arrow($file);
    } elsif ($label == 6) {
        ($o_total, @o_items) = parse_sentai($file);
    } elsif ($label == 7) {
        ($o_total, @o_items) = parse_kino($file);
    }

    #printf "\nORDER -- Vendor: '${label}', Total: \$%.2f\n", $o_total;
    $total += $o_total;
    push @items, @o_items;
}

# Count items and extract prices from order item list
my @prices = ();
my $n = 0;
foreach my $item (@items) {
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = split /\t/, $item;
    
    #printf "ITEM -- title='${title}', price='%.2f'\n", $price;
    push @prices, $price;
    $n++;
}

# Determine true cost
my @costs = sum_items($total, @prices);

print "\n\nOrder contains ${n} items\n\n";
# Print DB format for 'dvdlist' CGI script
print $db_header_row;
for (my $i = 0; $i < $n; $i++) {
    my ($title, $descr, $duration, $aspect, $discs, $region, $lang, $sub, $imdb, $date, $price, $asin, $url) = split /\t/, $items[$i];
    print "P: ${title}\t${descr}\t${duration}\t${aspect}\t${discs}\t${region}\t${lang}\t${sub}\t${imdb}\t${date}\t";
    printf "%.2f", $costs[$i];
    print "\t${asin}\t${url}\n";
}

