Currently, I am attempting to use the metadata from our streaming provider, StreamOn to send a request out to Last.FM to get the original sized album artwork. I am new to the world of APIs so it is rather confusing to me, however I am managing. My problem arises in the sending and receiving of the image over XML. In the code below, the contents of the metadata page are set as variables, which are then A.) displayed, and B.) used to look for the appropriate album artwork.
<?php
$request = file_get_contents('http://ckpk.streamon.fm/card.php');
$doubleQuotation = '"';
//Artist Request
$artistTitle = '"artist": "';
$artistTitlePosition = intval(strpos($request, $artistTitle));
$artistBeginningPosition = $artistTitlePosition + 11;
$artistEndingPosition = intval(strpos($request, $doubleQuotation, $artistBeginningPosition));
$artistName = substr($request, $artistBeginningPosition, $artistEndingPosition - $artistBeginningPosition);
echo '<b>' . $artistName . '</b>';
echo '<br />';
$artist = $artistName;
//Track Request
$trackTitle = '"title": "';
$trackTitlePosition = intval(strpos($request, $trackTitle));
$trackBeginningPosition = $trackTitlePosition + 10;
$trackEndingPosition = intval(strpos($request, $doubleQuotation, $trackBeginningPosition));
$trackName = substr($request, $trackBeginningPosition, $trackEndingPosition - $trackBeginningPosition);
echo '<i>' . $trackName . '</i>';
echo '<br />';
//Album Name Request
$albumTitle = '"album": "';
$albumTitlePosition = intval(strpos($request, $albumTitle));
$albumBeginningPosition = $albumTitlePosition + 10;
$albumEndingPosition = intval(strpos($request, $doubleQuotation, $albumBeginningPosition));
$albumName = substr($request, $albumBeginningPosition, $albumEndingPosition - $albumBeginningPosition);
echo $albumName;
$album = $albumName;
/*
* Last.FM Artwork Class
* Author: Caleb Mingle (#dentafrice)
* http://dentafrice.com
*/
class LastFM {
const API_KEY = "7facb82a2a573dd483d931044030e30c";
public static $size_map = array("small" => 0, "medium" => 1, "large" => 2, "extralarge" => 3, "mega" => 4);
public static function getArtwork($artist, $return_image = false, $size = "image_mega", $album) {
$artist = urlencode($artist);
$returnedInfo = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" . self::API_KEY . "&artist=" . $artist . "&album=" . $album . "&image=" . self::$size_map[$size] . "&format=json";
$returnedInfo = #file_get_contents($returnedInfo);
if(!$returnedInfo) {
return; // Artist lookup failed.
}
$xml = new SimpleXMLElement($xml);
$xml = $xml->artist;
$xml = $xml->image[self::$size_map[$size]];
return (!$return_image) ? $xml : '<img src="' . $xml . '" alt="' . urldecode($artist) . '" />';
}
}
$artwork = LastFM::getArtwork($artist, true, $size, $album);
if($artwork) {
echo $artwork;
}
else{
return;
}
?>
I temporarily styled the elements to distinguish between them and I will worry about the styling later. However, I would like to know how to go about using the data to send a request to the Lsat.FM servers and receive the image to then properly display it. It's different with StreamOn than with something else, such as ShoutCast.
According to your reply in the comments, my hypothesis is confirmed.
Because you are requesting the Last.FM API to return JSON, you get the error:
Fatal error: Uncaught exception 'Exception' with message 'String could
not be parsed as XML
Because it is JSON, not XML.
You can fix this in two ways. Either change the response format to XML or do not use SimpleXML but json_decode() instead.
The first is probably the easiest to adjust. Try to change the &format=json part to &format=xml
I haven't tested this but highly probably that this will be supported by Last.fm.
Also note that you don't supply an image size, and "image_mega" the default value is not a valid value (see API specs # http://www.last.fm/api/show/artist.getInfo). You probably want want to use "mega" not "image_mega"
Updated, working code using JSON:
<?php
class LastFM {
const API_KEY = "7facb82a2a573dd483d931044030e30c";
public static $size_map = array("small" => 0, "medium" => 1, "large" => 2, "extralarge" => 3, "mega" => 4);
public static function getArtwork($artist, $return_image = false, $size = "mega", $album) {
$artist = urlencode($artist);
$album = urlencode($album);
$returnedInfo = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key=" . self::API_KEY . "&artist=" . $artist . "&album=" . $album . "&image=" . self::$size_map[$size] . "&format=json";
$returnedInfo = #file_get_contents($returnedInfo);
if(!$returnedInfo) {
return; // Artist lookup failed.
}
$json = json_decode($returnedInfo, true);
$albumArt = $json["artist"]["image"][self::$size_map[$size]]["#text"];
return (!$return_image) ? print_r($json) : '<img src="' . $albumArt . '" alt="' . urldecode($artist) . '" />';
}
}
$artist = "Daft Punk";
$album = "Random Access Memories";
$size = "mega";
$artwork = LastFM::getArtwork($artist, true, $size, $album);
if($artwork) {
echo $artwork;
}
else{
return;
}
I know I rock. I choose JSON because I find it easier to work with. It returns exactly what you want, e.g.:
<img src="http://userserve-ak.last.fm/serve/500/10923145/Daft+Punk+6a00e398210d13883300e55ui6.png" alt="Daft Punk" />
Finally, I added urlencoding() for the album name, since I had issues with that in my example.
Working PHPFiddle
Related
I'm using the Petfinder API for Wordpress plugin. The plugin defaults to listing animals based on how old the Petfinder entries are, from oldest to newest. I'm trying to figure out a way to either do newest to oldest, or alphabetize based on animal names.
The data is loaded via the following code:
function get_petfinder_data($api_key, $shelter_id, $count, $pet = '') {
// If no specific pet is specified
if ( $pet == '' ) {
// Create request URL for all pets from the shelter
$request_url = 'http://api.petfinder.com/shelter.getPets?key=' . $api_key . '&count=' . $count . '&id=' . $shelter_id . '&status=A&output=full';
}
// If a specific pet IS specified
else {
// Create a request URL for that specific pet's data
$request_url = 'http://api.petfinder.com/pet.get?key=' . $api_key . '&id=' . $pet;
}
// Request data from Petfinder
$petfinder_data = #simplexml_load_file( $request_url );
// If data not available, don't display errors on page
if ($petfinder_data === false) {}
return $petfinder_data;
And the code that creates the list looks like this:
function get_all_pets($pets) {
foreach( $pets as $pet ) {
// Define Variables
$pet_name = get_pet_name($pet->name);
$pet_type = get_pet_type($pet->animal);
$pet_size = get_pet_size($pet->size);
$pet_age = get_pet_age($pet->age);
$pet_gender = get_pet_gender($pet->sex);
$pet_options = get_pet_options_list($pet);
$pet_description = get_pet_description($pet->description);
$pet_photo_thumbnail = get_pet_photos($pet, 'medium');
$pet_photo_all = get_pet_photos ($pet, 'large', false);
$pet_more_url = get_site_url() . '/adopt/adoptable-dogs/?view=pet-details&id=' . $pet->id;
$pet_pf_url = 'http://www.petfinder.com/petdetail/' . $pet->id;
// Create breed classes
$pet_breeds_condensed = '';
foreach( $pet->breeds->breed as $breed ) {
$pet_breeds_condensed .= pet_value_condensed($breed) . ' ';
}
// Create options classes
$pet_options_condensed = '';
foreach( $pet->options->option as $option ) {
$option = get_pet_option($option);
if ( $option != '' ) {
$pet_options_condensed .= pet_value_condensed($option) . ' ';
}
}
// Compile pet info
// Add $pet_options and $pet_breeds as classes and meta info
$pet_list .= '<div class="vc_col-sm-3 petfinder ' . pet_value_condensed($pet_age) . ' ' . pet_value_condensed($pet_gender) . ' ' . $pet_breeds_condensed . ' ' . $pet_options_condensed . '">' .
'<div class="dogthumbnail">' .
'' . $pet_photo_thumbnail . '<br>' .
'</div>' .
'<a class="dogname" href="' . $pet_more_url . '">' . $pet_name . '</a><br>' .
'<span> ' . $pet_age . ' • ' . $pet_gender . '<br>' .
'<div class="dogbreed">' . $pet_breeds_condensed . '</div>' .
'<a class="morelink" href="' . $pet_more_url . '">Learn More <i class="fas fa-angle-right"></i></a><br>' .
'</div>';
}
// Return pet list
return $pet_list;
Here's an example of the XML that the Petfinder API spits out (right now there are 25 pet entries in the full thing):
<petfinder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://api.petfinder.com/schemas/0.9/petfinder.xsd">
<header>
<version>0.1</version>
<timestamp>2018-06-11T17:32:34Z</timestamp>
<status>
<code>100</code>
<message/>
</status>
</header>
<lastOffset>25</lastOffset>
<pets>
<pet>
<id>31035385</id>
<shelterId>IL687</shelterId>
<shelterPetId/>
<name>Chanel</name>
<animal>Dog</animal>
<breeds>...</breeds>
<mix>yes</mix>
<age>Adult</age>
<sex>F</sex>
<size>M</size>
<options>...</options>
<description>...</description>
<lastUpdate>2014-12-14T17:59:49Z</lastUpdate>
<status>A</status>
<media>...</media>
<contact>...</contact>
</pet>
</pets>
</petfinder>
I'd like to sort all entries by either "name" or "lastUpdate". I've been looking at a lot of posts about sorting XML element objects but they either don't seem to work or I can't figure out how to apply them specifically to my code. I'm not super well-versed in this stuff, so any assistance is much appreciated!!
After a LOT of research and trial and error, I figured out how to organize alphabetically by animal name. Posting this in case anybody is ever trying to figure out the same.
First of all, I was wrong in my assumption of which sections I might need to be editing. It was actually line 723 of the plugin file. Here's how I modified the code for that section:
// Display a list of all available dogs
else {
// Access Petfinder Data
$petfinder_data = get_petfinder_data($api_key, $shelter_id, $count);
// If the API returns without errors
if( $petfinder_data->header->status->code == '100' ) {
// If there is at least one animal
if( count( $petfinder_data->pets->pet ) > 0 ) {
//Sort list of dogs ALPHABETICALLY by NAME
$petSXE = $petfinder_data->pets->children();
$petArray = array();
foreach($petSXE->pet as $d) {
$petArray[] = $d;
}
function name_cmp($a, $b) {
$va = (string) $a->name;
$vb = (string) $b->name;
if ($va===$vb) {
return 0;
}
return ($va<$vb) ? -1 : 1;
}
usort($petArray, 'name_cmp');
$pets = $petArray;
// Compile information that you want to include
$petfinder_list = get_type_list($pets).
get_age_list($pets) .
get_size_list($pets) .
get_gender_list($pets) .
get_options_list($pets) .
get_breed_list($pets) .
get_all_pets($pets);
}
This is adapting the solution I found in this thread: sort xml div by child node PHP SimpleXML
I have the following snippet:
$post_ids = array("307781172646235_739219686169046");
$quoted_post_ids = array();
foreach ($post_ids as $id) {
$quoted_post_ids[] = '"' . $id . '"';
}
$fql = urlencode(
"SELECT attachment FROM stream WHERE post_id IN(" . implode(',', $quoted_post_ids) . ")"
);
$all_photos_url = 'https://graph.facebook.com/fql?q=' . $fql . '&access_token=' . "XXXXXXXX";
$all_photos_result = drupal_http_request($all_photos_url);
$all_photos = json_decode($all_photos_result->data);
dpm($all_photos);
$actual_post = NULL;
foreach ($all_photos->data as $k => $post) {
$actual_post = array();;
foreach ($post->attachment->media as $photo) {
if (isset($photo->photo)) {
$photo_url = 'https://graph.facebook.com/' . $photo->photo->fbid;
$photo_result = drupal_http_request($photo_url);
$photo_info = json_decode($photo_result->data);
if (isset($photo_info->images[0])) {
$candidate_pic_url = $photo_info->images[0]->source;
if (!empty($candidate_pic_url) && valid_url($candidate_pic_url)) {
if (!isset($actual_post['picture_big'])) {
$actual_post['picture_big'] = array();
}
$actual_post['picture_big'][] = $candidate_pic_url;
$actual_post['picture_metadata'][] = $photo_info;
}
}
usleep(500);
}
}
dpm($actual_post);
}
The dpm() is just a Drupal-specific debug helper that outputs the variable. So the issue is that if I execute it at localhost, or on a staging server, it works, but on production, I do not have the image always via the graph API. Sometimes, I have, sometimes i does not have. It's a shared hosting at Gandi (https://www.gandi.net/hosting/simple) . Is it possible that the shared hosting itself becomes rate-limited?
Update:
On production, the second HTTP request gives: "Application request limit reached" OAuth Exception
How can I cache (using ModX's cacheManager), the dynamic placeholders i am generating here:
// recursive function to generate our un-ordered list of menu items
if(!function_exists('GenerateMenu')){
function GenerateMenu($level, $parent = 0, $wc, $wi, $we, $cs, $sc, $cl){
try {
$lvl = ++$level;
global $modx;
// Check for #1, should this be cached, #2 does it already exist in the cache
$cached = $modx->cacheManager->get('Navigator');
if($sc && isset($cached)){
// need to get the placeholders from cache - here somehow!
return $cached;
}else{
// get the site start
$siteStartId = $modx->getOption('site_start');
// Set our initial rows array
$rows = array();
// Run our query to get our menu items
$sql = 'Select `id`, `menutitle`, `uri`, `longtitle`, `parent`, `link_attributes`, `class_key`, `content`, `alias`, `introtext`
From `' . $modx->getOption(xPDO::OPT_TABLE_PREFIX) . 'site_content`
Where `deleted` = 0 AND `hidemenu` = 0 AND `published` = 1 AND `parent` = :parent
Order by `parent`, `menuindex`';
$query = new xPDOCriteria($modx, $sql, array(':parent' => $parent));
if ($query->stmt && $query->stmt->execute()) {
$rows = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
}
// do some cleanup
unset($query, $sql);
// make sure we have some rows, and then build our html for the menu
if($rows){
// grab a count of our results
$rCt = count($rows);
$cls = ($lvl > 1) ? 'sub-item-' . $lvl : 'main-item-' . $lvl;
$ret .= ' <ul class="' . $cls . '" id="' . $cls . '-' . $parent . '">' . "\r\n";
for($i = 0; $i < $rCt; ++$i){
// if this resource is a WebLink, show the content in it, as the URL for the href tag, otherwise, use the resource's URI
$url = ($rows[$i]['class_key'] == 'modWebLink') ? $rows[$i]['content'] : '/' . $rows[$i]['uri'];
// Check for the site's start id, if true, show a "blank" link, otherwise show the $url
$showUrl = ($siteStartId == $rows[$i]['id']) ? '/' : $url;
$la = (strlen($rows[$i]['link_attributes']) > 0) ? ' ' . $rows[$i]['link_attributes'] : null;
// Set some dynamic placeholders, they can only be used ont he pages that contain this snippet
$modx->toPlaceholders(array('Title-' . $rows[$i]['id'] => $rows[$i]['longtitle'],
'MenuTitle-' . $rows[$i]['id'] => $rows[$i]['menutitle'],
'URL-' . $rows[$i]['id'] => $showUrl),
'link');
$ret .= ' <li class="' . $cls . '" id="' . $rows[$i]['alias'] . '">' . "\r\n";
$ret .= ' <a href="' . $showUrl . '" title="' . $rows[$i]['longtitle'] . '"' . $la . '>' . $rows[$i]['menutitle'] . '</a>' . "\r\n";
$ret .= GenerateMenu($lvl, $rows[$i]['id']);
// Check for a snippet, and render it
$it = $rows[$i]['introtext'];
if($cs && IsSnippet($it)){
// if we find a snippet in the Summary field, run it, and attach it to our output
preg_match('/\[\[!?(.*)\]\]/', $it, $sm);
$ret .= $modx->runSnippet($sm[1]);
// clean up
unset($sm);
}
$ret .= ' </li>' . "\r\n";
}
$ret .= ' </ul>' . "\r\n";
}
// clean up
unset($rows);
// Check to see if we should cache it, if so, set it to cache, and apply the length of time it should be cached for: defaults to 2 hours
if($sc){
// NEED TO SET THE PLACEHOLDERS TO CACHE SOMEHOW
$modx->cacheManager->set('Navigator', $ret, $cl);
}
// return the menu
return $ret;
}
} catch(Exception $e) {
// If there was an error, make sure to write it out to the modX Error Log
$modx->log(modX::LOG_LEVEL_ERROR, '[Navigator] Error: ' . $e->getMessage());
return null;
}
}
}
The easiest solution may be pdoTools which allows you to establish caching at run time.
http://www.shawnwilkerson.com/modx/tags/pdotools/
Also, I do not believe resource placeholders are cached which is the best place to have your items cahced:
case '+':
$tagName= substr($tagName, 1 + $tokenOffset);
$element= new modPlaceholderTag($this->modx);
$element->set('name', $tagName);
$element->setTag($outerTag);
$elementOutput= $element->process($tagPropString);
break;
From lines 455-461 of https://github.com/modxcms/revolution/blob/master/core/model/modx/modparser.class.php#L455
You may notice the other tag types have:
$element->setCacheable($cacheable);
I cover the parser in Appendix D of my book. I found some issues in it in 2011 which Jason corrected.
Move toPlaceholders() to the end of your script and instead cache the array of placeholder data:
// attempt to retrieve placeholders from cache
$placeholders = $modx->cacheManager->get('Navigator');
// if not in cache, run your snippet logic and generate the data
if (empty($placeholders))) {
$placeholders = array(
'myplaceholder' => 'placeholder data'
);
$modx->cacheManager->set('Navigator', $placeholders, $cl);
}
// set placeholders for use by MODX
$modx->toPlaceholders($placeholders);
I have been trying to get the PayOne FrontEnd interface to accept the hash value from my request, to absolutely no avail. I have a support ticket open but need a solution relatively quick, so here I am.
The error returned is "Hashwert Nicht Korrekt" (Hash value incorrect).
Here is my code:
$request="authorization";
$portalid = 2017373;
$aid = 24413;
$key = "secretkeychangedforsecuritoyreasons"; // Key (configurable in the payment portal)
$id[1]= "PART_100";
$pr[1]= 2000;
$no[1] = 1;
$de[1] = "Registration Fee";
$va[1] = 19;
$amount = round($pr[1]*$no[1]);
$clearingtype = "cc";
$mode = "test";
$currency="EUR";
$reference="24393";
$customerid="24393";
$hash = md5(
$aid .
$amount .
$currency .
$customerid .
$clearingtype .
$de[1] .
$id[1] .
$mode .
$no[1] .
$portalid .
$pr[1] .
$reference .
$request .
$va[1] .
$key
);
$url="https://secure.pay1.de/frontend/?request=" . $request .
"&aid=" . $aid .
"&mode=" . $mode .
"&clearingtype=" . $clearingtype .
"&portalid=" . $portalid .
"&customerid=" . $customerid .
"¤cy=" . $currency .
"&amount=" . $amount .
"&reference=" . $reference .
"&id[1]=" . $id[1] .
"&pr[1]=" . $pr[1] .
"&no[1]=" . $no[1] .
"&de[1]=" . $de[1] .
"&va[1]=" . $va[1] .
"&hash=" . $hash;
header("Location: $url");
I have checked and re checked the docs and can find no errors in the way I am puttign it together. If I change single values like portalid, etc. it throws the appropriate error.
Any help would be appreciated.
I found the following section in the client-api-documentation:
Attention:
PAYONE Platform expects the calculated
hash
value
converted
to
lower
case;
e.g.
87dbc7c369b85b7a699adff1a2b27bab
Maybe you have some capital letter in your hash? I do an ".toLowerCase()" (in Java) on the encrypted hash.
One other option: you forgot some parameters.
on the first look i can't see the following: mid
We use the following:
$req['aid'] = 09876; // Sub-AccountID
$req['portalid'] = 6789012; // portal-ID
$req['mode'] = "live"; // Live || test
$req['request'] = "authorization"; // Request
$req['id[1]'] = $YourProductID; // e.g. articleno
$req['pr[1]'] = $singleprice;
$req['no[1]'] = $count; // count or pieces
$req['de[1]'] = $articledescription;
$req['amount'] = $summary; // price summary
$req['currency'] = "EUR";
$req['reference'] = $unique_ref.$YourProductId; //my unique is time()
$req['customerid'] = $userId;
$req['clearingtype'] = $clearing; // cc || wlt
$req['encoding'] = "UTF-8";
$req['targetwindow'] = "top";
ksort($req); //so i know its important to sort
//building the hash
foreach ($req as $key => $val) {
$req['hash'] = $req['hash'] . $val;
}
// all in md5() ... note your own payment ID
$req['hash'] = md5($req['hash'] . $YourPayOneID);
Hope it helps ;)
I am trying to work this function in wordpress theme the way where I can get value only what I want and not all to gather. I am not much familiar with using array in function so I need you expert help to make it works and I would really appreciate that.
Here is my code
function gallery_artist($arg=null, $arg2=null, $arg3=null){
global $png_gallery_meta;
$png_gallery_meta->the_meta();
$gallery = get_post_meta(get_the_ID(), $png_gallery_meta->get_the_id(), TRUE);
$gallery_value = $gallery['image_artist'];
$artist_info = $gallery_value;
$string = $artist_info;
$artist = substr($string, 0, stripos($string, "/") );
// getting first name and last name from user id
$author_first_name = get_userdata(basename($string))->first_name;
$author_last_name = get_userdata(basename($string))->last_name;
$author_full_name = $author_first_name . ' ' . $author_last_name;
$user_id = '<a href=" ' . get_author_posts_url(basename($string)) . ' " title="more submissions of ' . $author_first_name .' '. $author_last_name . ' " >' . $artist . '</a>';
$arg = $author_first_name;
echo $arg;
$arg2 = $author_last_name;
echo $arg2;
$arg3 = $user_id;
echo $arg3;
}
After than I want to use this function to get any value I want and not unnecessary to render all three to gather. Means if I want only first name than I pass value only for that and it will render only first name so on..
Here how I want to use something. but you can suggest any code and type of function I have no issue.
<?php call_user_func_array('gallery_artist', array('first_name','last_name','artist_id') ) ?>
Big Big thanks to you all..
Try replacing the bottom section with this:
if(!is_null($arg))
{
$arg = $author_first_name;
echo $arg;
}
if(!is_null($arg2))
{
$arg2 = $author_last_name;
echo $arg2;
}
if(!is_null($arg3))
{
$arg3 = $user_id;
echo $arg3;
}