Subversion Repositories cheapmusic

Rev

Rev 141 | Rev 154 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
include_once ('php/constants.php');

error_reporting(E_ALL);

// Get itunes listings
function get_amazon($query, $searchCondition) {
    $vendors = Vendors::getInstance();
    $config = $vendors->getVendor(Vendors::AMAZON);

    $needMatches = empty($_SESSION["discogs"]);
    $arrMusic = [];
    $arrDigital = [];
    $arrBooks = [];

    // Music
    $arrMusic = get_amazonCategory($config, $query, "Music", $needMatches);

    // Digital Music
    if ($_SESSION["filterMediaType"]["Digital"]) {
        $arrDigital = get_amazonCategory($config, $query, "DigitalMusic");
    }

    if ($_SESSION["filterMediaType"]["Book"]) {
        // Books
        $arrBooks = get_amazonCategory($config, $query, "Books");
    }

    return (array_merge($arrMusic, $arrDigital, $arrBooks));
}

// Get Amazon listings
function get_amazonCategory($config, $query, $searchIndex, $needMatches = false) {
    $numResults = $config['numResults'];

    if ($needMatches) {
        $_SESSION["discogs"] = "";
    }

    $response = getSearchCache("amazon", $query, $searchIndex);
    if ($response === false) {
        $serviceName = "ProductAdvertisingAPI";
        $region = "us-east-1";
        $accessKey = $config["access_key_id"];
        $secretKey = $config["secret_key"];
        $associate_tag = $config['associate_tag'];
$payload="{"
        ." \"Keywords\": \"$query\","
        ." \"Resources\": ["
        ."  \"Images.Primary.Medium\","
        ."  \"Images.Primary.Large\","
        ."  \"ItemInfo.ByLineInfo\","
        ."  \"ItemInfo.ContentInfo\","
        ."  \"ItemInfo.Classifications\","
        ."  \"ItemInfo.ExternalIds\","
        ."  \"ItemInfo.ManufactureInfo\","
        ."  \"ItemInfo.ProductInfo\","
        ."  \"ItemInfo.TechnicalInfo\","
        ."  \"ItemInfo.Title\","
        ."  \"Offers.Listings.Condition\","
        ."  \"Offers.Listings.Condition.SubCondition\","
        ."  \"Offers.Listings.DeliveryInfo.IsAmazonFulfilled\","
        ."  \"Offers.Listings.DeliveryInfo.IsFreeShippingEligible\","
        ."  \"Offers.Listings.DeliveryInfo.IsPrimeEligible\","
        ."  \"Offers.Listings.DeliveryInfo.ShippingCharges\","
        ."  \"Offers.Listings.MerchantInfo\","
        ."  \"Offers.Listings.Price\""
        ." ],"
        ." \"SearchIndex\": \"$searchIndex\","
        ." \"Availability\": \"Available\","
        ." \"Condition\": \"Any\","
        ." \"OfferCount\": 3,"
        ." \"SortBy\": \"Price:LowToHigh\","
        ." \"PartnerTag\": \"$associate_tag\","
        ." \"PartnerType\": \"Associates\","
        ." \"Marketplace\": \"www.amazon.com\""
        ."}";
        $host = "webservices.amazon.com";
        $uriPath = "/paapi5/searchitems";

        $awsv4 = new AwsV4($accessKey, $secretKey);
        $awsv4->setRegionName($region);
        $awsv4->setServiceName($serviceName);
        $awsv4->setPath($uriPath);
        $awsv4->setPayload($payload);
        $awsv4->setRequestMethod("POST");
        $awsv4->addHeader('content-encoding', 'amz-1.0');
        $awsv4->addHeader('content-type', 'application/json; charset=utf-8');
        $awsv4->addHeader('host', $host);
        $awsv4->addHeader('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems');
        $headers = $awsv4->getHeaders();
        $headerArr = [];
        foreach ($headers as $key => $value) {
            $headerArr[] = $key . ': ' . $value;
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
        curl_setopt($ch, CURLOPT_URL, 'https://' . $host . $uriPath);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_REFERER, 'https://' . $host . $uriPath);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_POST, 1);
        $response = curl_exec($ch);
        curl_close($ch);

        saveSearchCache("amazon", $query, $searchIndex, $response);
    }

    $parsed_json = json_decode($response);
//echo "<br><pre>";print_r($parsed_json);echo "</pre>";
    $arr = [];

    if (isset($parsed_json->Errors)) {
        foreach ($parsed_json->Errors as $error) {
            if ($error->Code != "NoResults") {
                my_error_log("Amazon Search - " . $error->Message . " (" . $error->Code . ")");
            }
        }
        return [];
    } else if (!isset($parsed_json->SearchResult)) {
        my_error_log("Amazon Search - No Search Result.");
        return [];
    }

    if ($needMatches) {
        $cnt = 0;

        $xh = new Html;
        $xh->init($_SESSION["htmlIndent"]);
        startMatches($xh);
    }

    $listings = 0;
    // If the response was loaded, parse it and store in array
    foreach ($parsed_json->SearchResult->Items as $current) {
        if (isset($current->ItemInfo->ExternalIds->UPCs)) {
            $barcode = (string)$current->ItemInfo->ExternalIds->UPCs->DisplayValues[0];
        }
        else if (isset($current->ItemInfo->ExternalIds->EANs)) {
            $barcode = (string)$current->ItemInfo->ExternalIds->EANs->DisplayValues[0];
        }
        else if (isset($current->ItemInfo->ExternalIds->ISBNs)) {
            $barcode = (string)$current->ItemInfo->ExternalIds->ISBNs->DisplayValues[0];
        }
        else if (isset($current->ItemInfo->ExternalIds->EISBNs)) {
            $barcode = (string)$current->ItemInfo->ExternalIds->EISBNs->DisplayValues[0];
        }
        else {
            $barcode = "";
        }
        $barcodeType = clsLibGTIN::GTINCheck($barcode, false, 1);

        $pic = !empty($current->Images->Primary->Medium->URL) ? (string)$current->Images->Primary->Medium->URL : "images/no-image-available.jpg";
        $pic = str_replace('http://', 'https://', $pic);
        if (empty($pic)) {
            continue;
        }

        if (strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "Audio CD") !== false) {
            $mediaType = "CD";
        }
        else if (strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "MP3 Music") !== false) {
            $mediaType = "Digital";
        }
        else if (strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "Vinyl") !== false) {
            $mediaType = "Record";
        }
        else if (strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "Paperback") !== false || strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "Sheet") !== false || strpos($current->ItemInfo->Classifications->Binding->DisplayValue, "Hardcover") !== false) {
            $mediaType = "Book";
        }
        else {
            continue;
        }

        if ($needMatches) {
            // bugbug: check maxMasterCnt?
            addMatch($xh, $current, ++$cnt, $mediaType);
        }

        $title = (string)$current->ItemInfo->Title->DisplayValue;
        $artists = getArtists($current);
        $contributers = getContributers($current);
        if (!empty($artists)) {
            $title .= " by " . join(", ", $artists);
        }
        else if (!empty($contributers)) {
            $title .= " by " . join(", ", $contributers);
        }

        $url = str_replace('http://', 'https://', (string)$current->DetailPageURL);

        if (empty($url)) {
            continue;
        }

        $bestOffer = false;

        $url = str_replace('http://', 'https://', (string)$current->DetailPageURL);
        $url .= "&condition=";

        if (!empty($current->Offers->Listings)) {
            foreach ($current->Offers->Listings as $offer) {
                $country = 'US';
                if (isset($offer->MerchantInfo->DefaultShippingCountry)) {
                    $country = $offer->MerchantInfo->DefaultShippingCountry;
                }
                $merchantName = "Amazon";
                if (strpos($offer->MerchantInfo->Name, "Amazon") === false) {
                    $merchantName .= " Marketplace";
                }

                $condition = (string)$offer->Condition->Value;
                $url .= $condition;
                $detailCondition = $condition;
                if ($condition == "Collectible") {
                    $condition = 'Used';
                    $detailCondition = "Collectible";
                }
                if ($condition == "Refurbished") {
                    $condition = 'Used';
                    $detailCondition = "Refurbished";
                }
                $currency = (string)$offer->Price->Currency;
                $price = number_format(floatval($offer->Price->Amount) , 2, '.', '');

                if ($price <= "0.00") {
                    continue;
                }

                $timeLeft = 0;
                $listingType = 'Fixed';
                $location = 'US';
                $zip = '';
                $feedbackScore = - 1;
                $feedbackPercent = - 1;
                $sellerName = "";
                $handlingTime = 0;
                if ($offer->DeliveryInfo->IsPrimeEligible === true) {
                    $sellerName = "Prime";
                }
                if ($mediaType == "Digital") {
                    $shippingCost = 0.00;
                    $shippingEstimated = false;
                    $shippingCurrency = 'USD';
                }
                else {
                    $shippingCost = 3.99; // bugbug
                    $shippingEstimated = true; // bugbug
                    $shippingCurrency = 'USD';
                }
                $freeShippingCap = !empty($offer->DeliveryInfo->IsFreeShippingEligible) ? 25 : 0;

                if (++$listings > $numResults) {
                    continue;
                }

                $arr[] = array(
                    "Merchant" => $merchantName,
                    "Condition" => $condition,
                    "Title" => $title,
                    "Barcode" => $barcode,
                    "BarcodeType" => $barcodeType,
                    "Image" => $pic,
                    "URL" => $url,
                    "MediaType" => $mediaType,
                    "DetailCondition" => $detailCondition,
                    "Country" => $country,
                    "BestOffer" => $bestOffer,
                    "TimeLeft" => $timeLeft,
                    "Price" => $price,
                    "Currency" => $currency,
                    "ListingType" => $listingType,
                    "Location" => $location,
                    "Zip" => $zip,
                    "FeedbackScore" => $feedbackScore,
                    "FeedbackPercent" => $feedbackPercent,
                    "SellerName" => $sellerName,
                    "HandlingTime" => $handlingTime,
                    "ShippingCost" => $shippingCost,
                    "ShippingEstimated" => $shippingEstimated,
                    "ShippingCurrency" => $shippingCurrency,
                    "FreeShippingCap" => $freeShippingCap,
                    "Show" => true,
                    "Details" => ""
                );
            }
        }
    }

    if ($needMatches) {
        if ($cnt = 0) {
            $_SESSION["discogs"] = "";
        }
        else {
            endMatches($xh);
            
            $_SESSION["discogs"] = $xh->flush();
            error_log(print_r($_SESSION["discogs"], 1));
        }
    }

    // If the response does not indicate 'Success,' log the error(s)
    /******************
    else {
        my_error_log($url);
        if (!empty($parsed_json->OperationRequest->Errors)) {
            foreach($parsed_json->OperationRequest->Errors->Error as $error){
                my_error_log($error->Message . " (" . $error->Code . ")");
            }
        } else if (!empty($parsed_json->Errors)) {
            foreach($parsed_json->OperationRequest->Errors->Error as $error){
                my_error_log($error->Message . " (" . $error->Code . ")");
            }
        } else if (!empty($parsed_json->Error)) {
            my_error_log($parsed_json->Error->Message . " (" . $parsed_json->Error->Code . ")");
        } else {
            my_error_log(print_r($result, 1));
        }
    }
    ******************/

    return $arr;
}

function startMatches(&$xh) {
    $xh->add_attribute("class", "container-fluid bg-light");
    $xh->add_attribute("id", "discogsTable");
    $xh->tag('div');
    $xh->add_attribute("class", "text-center py-2");
    $xh->tag('h2', "Matching Albums");

    $xh->add_attribute("id", "discogsDeckForm");
    $xh->add_attribute("method", "post");
    $xh->add_attribute("action", "/index.php");
    $xh->tag('form');
    $xh->insert_code(inputSessionTab());
    $xh->insert_code(inputNonce());
    $xh->add_attribute("id", "discogsTitle");
    $xh->add_attribute("type", "hidden");
    $xh->add_attribute("name", "discogsTitle");
    $xh->add_attribute("value", "");
    $xh->single_tag('input');
    $xh->add_attribute("id", "discogsArtist");
    $xh->add_attribute("type", "hidden");
    $xh->add_attribute("name", "discogsArtist");
    $xh->add_attribute("value", "");
    $xh->single_tag('input');
    $xh->add_attribute("id", "discogsBarcode");
    $xh->add_attribute("type", "hidden");
    $xh->add_attribute("name", "discogsBarcode");
    $xh->add_attribute("value", "");
    $xh->single_tag('input');
    $xh->add_attribute("id", "discogsDeck");
    $xh->add_attribute("class", "card-deck");
    $xh->tag('div');
}

function endMatches(&$xh) {
    $xh->insert_code(discogsEvents());

    $xh->close(); // div
    $xh->close(); // form>";
    $xh->close(); // div>";
}

function addMatch(&$xh, $item, $cnt, $mediaType) {
    $xhmod = new Html;
    $xhmod->init($_SESSION["htmlIndent"]);

    $artists = getArtists($item);
    $contributers = getContributers($item);

    $label = "";
    if (!empty($item->ItemInfo->ByLineInfo->Manufacturer)) {
        $label = $item->ItemInfo->ByLineInfo->Manufacturer->DisplayValue;
    }

    $languages = [];
    if (!empty($item->ItemInfo->ContentInfo->Languages)) {
        foreach ($item->ItemInfo->ContentInfo->Languages->DisplayValues as $language) {
            if ((string)$language->Type != "Unknown") {
                $languages[] = $language->DisplayValue . " (" . $language->Type . ")";
            }
        }
    }

    $genres = [];
    if (!empty($item->ItemInfo->Genre)) {
        foreach($item->ItemInfo->Genre as $genre) {
            $genres[] = join(" ", array_map('ucfirst', explode('-', $genre)));
        }
    }

    $runningTime = "";
    if (!empty($item->ItemInfo->RunningTime)) {
        if ($mediaType != 'Digital') {
            $runningTime = (string)$item->ItemInfo->RunningTime . " minutes";
        } else {
            $runningTime = gmdate("H:i:s", (int)$item->ItemInfo->RunningTime);
        }
    }

    $xh->add_attribute("class", "card mx-auto discogs-card");
    $xh->tag('div');
    $xh->add_attribute("class", "card-header bg-secondary d-flex");
    $xh->tag('div');
    $searchArtists = "";
    $wlArtists = "";
    if (!empty($artists)) {
        if (count($artists) < 5) {
            $wlArtists = join(", ", $artists);
            if (!empty($artists) && $artists[0] != 'Various') {
                $searchArtists = join(" ", $artists);
            }
        } else {
            $wlArtists = "Various Artists";
        }
    }
    else if (!empty($contributers)) {
        if (count($contributers) < 5) {
            $wlArtists = join(", ", $contributers);
            if (!empty($contributers) && $contributers[0] != 'Various') {
                $searchArtists = join(" ", $contributers);
            }
        } else {
            $wlArtists = "Various Artists";
        }
    }

    $str = htmlentities((string)$item->ItemInfo->Title->DisplayValue);
    if (!empty($wlArtists)) {
        $str .= " by " . htmlentities($wlArtists);
    }
    $xh->add_attribute("class", "card-title font-weight-bold small flex-grow-1");
    $xh->tag('p', $str);
    $xh->close(); // div

    $thumbnail = !empty($item->Images->Primary->Medium->URL) ? (string)$item->Images->Primary->Medium->URL : "images/no-image-available.jpg";

    $xh->add_attribute("class", "card-body d-flex justify-content-center align-items-center p-1 m-0");
    $xh->tag('div');
    $xh->add_attribute("class", "btn p-0 m-0 lazyload");
    $xh->add_attribute("src", PIXEL);
    $xh->add_attribute("data-src",  $thumbnail);
    $xh->add_attribute("data-toggle", "modal");
    $xh->add_attribute("data-target", "#masterModal" . $cnt);
    $xh->add_attribute("title", "Album Information");
    $xh->add_attribute("data-toggle2", "tooltip");
    $xh->add_attribute("alt", "Cover Thumbnail");
    $xh->single_tag('img');
    $xh->close(); // div
    $xh->add_attribute("class", "card-footer form-row btn-group-sm bg-secondary p-0 m-0");
    $xh->tag('div');
    $xh->add_attribute("class", "col-3 btn-group-sm p-0 m-0");
    $xh->tag('div');

    $barcode = "";
    $tmpBarcode = "";
    if (isset($item->ItemInfo->ExternalIds->UPCs)) {
        $tmpBarcode = (string)$item->ItemInfo->ExternalIds->UPCs->DisplayValues[0];
    }
    else if (isset($item->ItemInfo->ExternalIds->EANs)) {
        $tmpBarcode = (string)$item->ItemInfo->ExternalIds->EANs->DisplayValues[0];
    }
    else if (isset($item->ItemInfo->ExternalIds->ISBNs)) {
        $tmpBarcode = (string)$item->ItemInfo->ExternalIds->ISBNs->DisplayValues[0];
    }
    else if (isset($item->ItemInfo->ExternalIds->EISBNs)) {
        $tmpBarcode = (string)$item->ItemInfo->ExternalIds->EISBNs->DisplayValues[0];
    }
    $barcodeType = clsLibGTIN::GTINCheck($tmpBarcode, false, 1);
    if (!empty($barcodeType)) {
        $barcode = $tmpBarcode;
    }

    if (isLoggedIn()) {
        $checkWlFlag = checkWishlist('asin', $item->ASIN);
        $wlArr = array(
            'asin' => (string)$item->ASIN,
            'title' => (string)$item->ItemInfo->Title->DisplayValue,
            'artist' => $wlArtists,
            'barcode' => $barcode,
            'thumbnail' => $thumbnail,
            'url' => (string)$item->DetailPageURL
        );
        $wl = base64_encode(json_encode($wlArr));

        if (!$checkWlFlag) {
            $xh->add_attribute("id", "wl" . $cnt . "Btn");
            $xh->add_attribute("data-user", $_SESSION['sessData']['userID']);
            $xh->add_attribute("data-cnt", $cnt);
            $xh->add_attribute("data-wl", $wl);
        }

        $xh->add_attribute("type", "button");
        $xh->add_attribute("class", "btn btn-info btn-sm");
        $xh->add_attribute("title", $checkWlFlag ? "Already on Wishlist" : "Add to Wishlist");
        $xh->add_attribute("aria-label", $checkWlFlag ? "Already on Wishlist" : "Add to Wishlist");
        $xh->add_attribute("data-toggle", "tooltip");
        $xh->tag('button');
        $xh->add_attribute("class", "material-icons");
        $xh->tag('i', $checkWlFlag ? "library_add_check" : "library_add");
        $xh->close(); // button
    }
    $xh->close(); // div

    $xh->add_attribute("class", "col-3 btn-group-sm p-0 m-0");
    $xh->tag('div');
    $xh->close(); // div

    $searchTitle = 'Searching for:<br><br><strong>' . htmlentities((string)$item->ItemInfo->Title->DisplayValue) . ' by ' . htmlentities($wlArtists) . '</strong>';
    if (!empty($barcode)) {
        $searchTitle .= " (" . displayBarcode($barcode) . ")";
    }

    $xh->add_attribute("class", "col-6 btn-group p-0 m-0");
    $xh->tag('div');
    $xh->add_attribute("id", "discogsSearch" . $cnt . "Btn");
    $xh->add_attribute("type", "submit");
    $xh->add_attribute("name", "submitBtn");
    $xh->add_attribute("value", "discogsSearch");
    $xh->add_attribute("class", "btn btn-success btn-sm");
    $xh->add_attribute("title", "Search for Store Offers");
    $xh->add_attribute("aria-label", "Search for Store Offers");
    $xh->add_attribute("data-toggle", "tooltip");
    $xh->add_attribute("data-title", htmlentities((string)$item->ItemInfo->Title->DisplayValue));
    $xh->add_attribute("data-artist", htmlentities($searchArtists));
    $xh->add_attribute("data-barcode", $barcode);
    $xh->add_attribute("data-search-title", $searchTitle);
    $xh->tag('button');
    $xh->add_attribute("class", "material-icons material-text");
    $xh->tag('i', "search");
    $xh->tag('span', "Offer");
    $xh->close(); // button
    $xh->close(); // div

    $xh->add_attribute("id", "wishlistAdd" . $cnt);
    $xh->tag('span', "");
    $xh->close(); // div

    $xhmod->add_attribute("id", "masterModal" . $cnt);
    $xhmod->add_attribute("class", "modal");
    $xhmod->tag('div');
    $xhmod->add_attribute("class", "modal-dialog");
    $xhmod->tag('div');
    $xhmod->add_attribute("class", "modal-content");
    $xhmod->tag('div');
    $xhmod->add_attribute("class", "modal-header bg-secondary");
    $xhmod->tag('div');
    $xhmod->add_attribute("class", "modal-title mx-auto display-6");
    $xhmod->tag('p', htmlentities((string)$item->ItemInfo->Title->DisplayValue) . " by " . htmlentities($wlArtists));
    $xhmod->add_attribute("type", "button");
    $xhmod->add_attribute("class", "close");
    $xhmod->add_attribute("data-dismiss", "modal");
    $xhmod->tag('button');
    $xhmod->add_attribute("class", "material-icons btn-dismiss");
    $xhmod->tag('i', "cancel_presentation");
    $xhmod->close(); // button
    $xhmod->close(); // div

    $xhmod->add_attribute("class", "modal-body mx-auto");
    $xhmod->tag('div');

    if (!empty($item->Images->Primary->Large->URL)) {
        $xhmod->add_attribute("class", "img-fluid d-flex mx-auto mb-4 lazyload");
        $xhmod->add_attribute("src", PIXEL);
        $xhmod->add_attribute("data-src", (string)$item->Images->Primary->Large->URL);
        $xhmod->add_attribute("alt", "Cover Image");
        $xhmod->single_tag('img');
    }

    $xhmod->add_attribute("class", "table-borderless table-condensed small mx-auto");
    $xhmod->tag('table');

    $xhmod->tag('tr');
    $xhmod->add_attribute("class", "px-1");
    $xhmod->tag('td', "Title:");
    $xhmod->add_attribute("class", "px-1");
    $xhmod->tag('td', htmlentities((string)$item->ItemInfo->Title->DisplayValue));
    $xhmod->close(); // tr

    $xhmod->tag('tr');
    $xhmod->add_attribute("class", "px-1");
    $xhmod->tag('td', "Artist:");
    $xhmod->add_attribute("class", "px-1");
    $xhmod->tag('td', htmlentities(join(", ", $artists)));
    $xhmod->close(); // tr

    if (!empty($contributers)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Creator:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $contributers)));
        $xhmod->close(); // tr
    }

    if (!empty($actors)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Actor:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $actors)));
        $xhmod->close(); // tr
    }

    if (!empty($directors)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Director:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $directors)));
        $xhmod->close(); // tr
    }

    if (!empty($authors)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Author:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $authors)));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->Classifications->Binding)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Type:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->Classifications->Binding->DisplayValue));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->MediaType)) { //
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Media Type:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->MediaType));
        $xhmod->close(); // tr
    }

    if (!empty($barcode)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Barcode:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', displayBarcode($barcode));
        $xhmod->close(); // tr
    }

    if (!empty($label)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Label:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities($label));
        $xhmod->close(); // tr
    }

    if (!empty($languages)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Languages:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $languages)));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->ContentInfo->PublicationDate)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Publication Date:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->ContentInfo->PublicationDate->DisplayValue));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->ContentInfo->ReleaseDate)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Release Date:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->ContentInfo->ReleaseDate->DisplayValue));
        $xhmod->close(); // tr
    }

    if (!empty($genres)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Genre:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities(join(", ", $genres)));
        $xhmod->close(); // tr
    }
    
    if (!empty($item->ItemInfo->ContentInfo->UnitCount->DisplayValue)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Number of Discs:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->ContentInfo->UnitCount->DisplayValue));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->ContentInfo->PagesCount->DisplayValue)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Number of Pages:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->ContentInfo->PagesCount->DisplayValue));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->RunningTime)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Running Time:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities($runningTime));
        $xhmod->close(); // tr
    }

    if (!empty($item->ItemInfo->Edition)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Edition:");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', htmlentities((string)$item->ItemInfo->Edition));
        $xhmod->close(); // tr
    }

    if (!empty($item->Tracks)) {
        $xhmod->tag('tr');
        $xhmod->add_attribute("colspan", "2");
        $xhmod->add_attribute("class", "px-1");
        $xhmod->tag('td', "Tracks:");
        $xhmod->close(); // tr

        $xhmod->tag('tr');
        $xhmod->add_attribute("colspan", "2");
        $xhmod->tag('td');
        $xhmod->add_attribute("class", "pl-3 pt-0 small list-unstyled");
        $xhmod->tag('ul');
        foreach ($item->Tracks->Disc as $disc) {
            if ((int)$item->ItemInfo->ContentInfo->UnitCount->DisplayValue > 1 && !empty($disc->attributes())) {
                $xhmod->add_attribute("class", "font-weight-bold");
                $xhmod->tag('li', "Disc " . $disc->attributes() . ":");
            }

            foreach($disc->Track as $track) {
                $xhmod->tag('li', (!empty($track->attributes()) ? $track->attributes() . ") " : "") . htmlentities($track));
            }
        }
        $xhmod->close(); // ul
        $xhmod->close(); // td
        $xhmod->close(); // tr
    }

    $xhmod->close(); // table
    $xhmod->close(); // div
    $xhmod->add_attribute("class", "modal-footer bg-secondary justify-content-between");
    $xhmod->tag('div');
    $xhmod->add_attribute("class", "float-right");
    $xhmod->tag('span');
    $xhmod->add_attribute("type", "button");
    $xhmod->add_attribute("class", "btn btn-danger");
    $xhmod->add_attribute("data-dismiss", "modal");
    $xhmod->tag('button', "Close");
    $xhmod->close(); // span
    $xhmod->close(); // div
    $xhmod->close(); // div
    $xhmod->close(); // div
    $xhmod->close(); // div
    $html = $xhmod->flush();
    //error_log(print_r($html, 1));

    $xh->insert_code($html);
    $xh->close(); // div

    return;
}

function getArtists($item) {
    $artists = [];

    if (!empty($item->ItemInfo->Artist)){
        $artists[] = $item->ItemInfo->Artist;
    } else if (!empty($item->ItemInfo->ByLineInfo->Contributors)) {
        foreach ($item->ItemInfo->ByLineInfo->Contributors as $artist) {
            if ((string)$artist->Role == "Artist") {
                $artists[] = $artist->Name;
            }
        }
    }

    return $artists;
}

function getContributers($item) {
    $contributers = [];

    if (!empty($item->ItemInfo->ByLineInfo->Contributors)) {
        foreach ($item->ItemInfo->ByLineInfo->Contributors as $creator) {
            if ((string)$creator->Role == "Primary Contributor") {
                $contributers[] = $creator->Name;
            }
            else {
                $contributers[] = $creator->Name . " (" . $creator->Role . ")";
            }
        }
    }

    return $contributers;
}

class AwsV4 {

    private $accessKey = null;
    private $secretKey = null;
    private $path = null;
    private $regionName = null;
    private $serviceName = null;
    private $httpMethodName = null;
    private $queryParametes = array();
    private $awsHeaders = array();
    private $payload = "";

    private $HMACAlgorithm = "AWS4-HMAC-SHA256";
    private $aws4Request = "aws4_request";
    private $strSignedHeader = null;
    private $xAmzDate = null;
    private $currentDate = null;

    public function __construct($accessKey, $secretKey) {
        $this->accessKey = $accessKey;
        $this->secretKey = $secretKey;
        $this->xAmzDate = $this->getTimeStamp();
        $this->currentDate = $this->getDate();
    }

    function setPath($path) {
        $this->path = $path;
    }

    function setServiceName($serviceName) {
        $this->serviceName = $serviceName;
    }

    function setRegionName($regionName) {
        $this->regionName = $regionName;
    }

    function setPayload($payload) {
        $this->payload = $payload;
    }

    function setRequestMethod($method) {
        $this->httpMethodName = $method;
    }

    function addHeader($headerName, $headerValue) {
        $this->awsHeaders[$headerName] = $headerValue;
    }

    private function prepareCanonicalRequest() {
        $canonicalURL = "";
        $canonicalURL .= $this->httpMethodName . "\n";
        $canonicalURL .= $this->path . "\n" . "\n";
        $signedHeaders = '';
        foreach ($this->awsHeaders as $key => $value) {
            $signedHeaders .= $key . ";";
            $canonicalURL .= $key . ":" . $value . "\n";
        }
        $canonicalURL .= "\n";
        $this->strSignedHeader = substr($signedHeaders, 0, -1);
        $canonicalURL .= $this->strSignedHeader . "\n";
        $canonicalURL .= $this->generateHex($this->payload);
        return $canonicalURL;
    }

    private function prepareStringToSign($canonicalURL) {
        $stringToSign = '';
        $stringToSign .= $this->HMACAlgorithm . "\n";
        $stringToSign .= $this->xAmzDate . "\n";
        $stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n";
        $stringToSign .= $this->generateHex($canonicalURL);
        return $stringToSign;
    }

    private function calculateSignature($stringToSign) {
        $signatureKey = $this->getSignatureKey($this->secretKey, $this->currentDate, $this->regionName, $this->serviceName);
        $signature = hash_hmac("sha256", $stringToSign, $signatureKey, true);
        $strHexSignature = strtolower(bin2hex($signature));
        return $strHexSignature;
    }

    public function getHeaders() {
        $this->awsHeaders['x-amz-date'] = $this->xAmzDate;
        ksort($this->awsHeaders);

        // Step 1: CREATE A CANONICAL REQUEST
        $canonicalURL = $this->prepareCanonicalRequest();

        // Step 2: CREATE THE STRING TO SIGN
        $stringToSign = $this->prepareStringToSign($canonicalURL);

        // Step 3: CALCULATE THE SIGNATURE
        $signature = $this->calculateSignature($stringToSign);

        // Step 4: CALCULATE AUTHORIZATION HEADER
        if ($signature) {
            $this->awsHeaders['Authorization'] = $this->buildAuthorizationString($signature);
            return $this->awsHeaders;
        }
    }

    private function buildAuthorizationString($strSignature) {
        return $this->HMACAlgorithm . " " . "Credential=" . $this->accessKey . "/" . $this->getDate() . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "," . "SignedHeaders=" . $this->strSignedHeader . "," . "Signature=" . $strSignature;
    }

    private function generateHex($data) {
        return strtolower(bin2hex(hash("sha256", $data, true)));
    }

    private function getSignatureKey($key, $date, $regionName, $serviceName) {
        $kSecret = "AWS4" . $key;
        $kDate = hash_hmac("sha256", $date, $kSecret, true);
        $kRegion = hash_hmac("sha256", $regionName, $kDate, true);
        $kService = hash_hmac("sha256", $serviceName, $kRegion, true);
        $kSigning = hash_hmac("sha256", $this->aws4Request, $kService, true);

        return $kSigning;
    }

    private function getTimeStamp() {
        return gmdate("Ymd\THis\Z");
    }

    private function getDate() {
        return gmdate("Ymd");
    }
}