This repository has been archived by the owner on Mar 23, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
29b94f0
commit 4d9dfb5
Showing
4 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<?php | ||
/** | ||
* Class MixesDBTrackSearch | ||
* | ||
* @created 30.08.2023 | ||
* @author smiley <[email protected]> | ||
* @copyright 2023 smiley | ||
* @license MIT | ||
*/ | ||
|
||
namespace chillerlan\OAuthExamples\Providers\Spotify; | ||
|
||
use chillerlan\HTTP\Utils\MessageUtil; | ||
use function array_column; | ||
use function array_map; | ||
use function array_merge; | ||
use function explode; | ||
use function file_get_contents; | ||
use function implode; | ||
use function json_decode; | ||
use function preg_replace; | ||
use function sprintf; | ||
use function str_replace; | ||
use function strtotime; | ||
use function trim; | ||
use function usleep; | ||
|
||
/** | ||
* | ||
*/ | ||
class MixesDBTrackSearch extends SpotifyClient{ | ||
|
||
/** | ||
* search tracks on spotify from the given mixesdb track lists | ||
*/ | ||
public function getTracks( | ||
string $clubnightsJSON, | ||
int $since, | ||
int $until, | ||
array $find = [], | ||
int $limit = 5, | ||
bool $playlistPerSet = false, | ||
):void{ | ||
$clubnights = json_decode(file_get_contents($clubnightsJSON), true); | ||
$tracks = []; | ||
|
||
foreach($clubnights as $date => $sets){ | ||
$date = strtotime($date); | ||
// skip by date | ||
if($date < $since || $date > $until){ | ||
continue; | ||
} | ||
|
||
foreach($sets as $name => $set){ | ||
// skip by inclusion list | ||
if($this->setContains($name, $find)){ | ||
continue; | ||
} | ||
|
||
$this->logger->info($name); | ||
$setTracks = []; | ||
|
||
foreach($set as $track){ | ||
$track = $this->cleanTrack($track); | ||
|
||
if(empty($track)){ | ||
continue; | ||
} | ||
|
||
$this->logger->info(sprintf('search: %s', $track)); | ||
|
||
$response = $this->spotify->request('/v1/search', [ | ||
'q' => $this->getSearchTerm($track), | ||
'type' => 'track', | ||
'limit' => $limit, | ||
'market' => $this->market, | ||
]); | ||
|
||
usleep(self::sleepTimer); | ||
|
||
if($response->getStatusCode() !== 200){ | ||
continue; | ||
} | ||
|
||
$data = MessageUtil::decodeJSON($response); | ||
|
||
foreach($data->tracks->items as $i => $item){ | ||
$setTracks[$item->id] = $item->id; | ||
|
||
$this->logger->info(sprintf('found: [%s][%s] %s - %s', ++$i, $item->id, implode(', ', array_column($item->artists, 'name')), $item->name)); | ||
} | ||
|
||
} | ||
|
||
if($playlistPerSet){ | ||
$playlistID = $this->createPlaylist($name, ''); | ||
$this->addTracks($playlistID, $setTracks); | ||
} | ||
|
||
$tracks = array_merge($tracks, $setTracks); | ||
} | ||
|
||
} | ||
|
||
$playlistID = $this->createPlaylist('mixesdb search result', ''); | ||
$this->addTracks($playlistID, $tracks); | ||
} | ||
|
||
/** | ||
* check a string for the occurence of any in the given array of needles | ||
*/ | ||
protected function setContains(string $haystack, array $needles):bool{ | ||
$haystack = mb_strtolower($haystack); | ||
|
||
return !empty($needles) && str_replace(array_map('mb_strtolower', $needles), '', $haystack) === $haystack; | ||
} | ||
|
||
/** | ||
* clean any unwanted symbols/strings from the track name | ||
*/ | ||
protected function cleanTrack(string $track):string{ | ||
// strip time codes [01:23] and record IDs [EYE Q - 001] from name | ||
return trim(preg_replace(['/^\[[\d:?]+\] /', '/ \[[^]]+\]/'], '', $track), ' -?'); | ||
} | ||
|
||
/** | ||
* prepare the spotify search term | ||
*/ | ||
protected function getSearchTerm(string $track):string{ | ||
$at = explode(' - ', $track, 2); // artist - track | ||
|
||
return match (count($at)){ | ||
1 => sprintf('artist:%1$s track:%1$s', $at[0]), | ||
2 => sprintf('artist:%s track:%s', $at[0], $at[1]), | ||
}; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
/** | ||
* mixesdb-scrape.php | ||
* | ||
* Not an actual OAuth API example, just a helper script | ||
* | ||
* Unfortunately, mixesdb doesn't have the mediawiki API activated, so we gotta scrape the data manually. | ||
* | ||
* @see https://www.mixesdb.com/w/Category:Clubnight | ||
* | ||
* @created 28.03.2023 | ||
* @author smiley <[email protected]> | ||
* @copyright 2023 smiley | ||
* @license MIT | ||
* | ||
* @noinspection PhpComposerExtensionStubsInspection | ||
*/ | ||
|
||
namespace chillerlan\OAuthExamples\Providers\Spotify; | ||
|
||
use chillerlan\HTTP\Utils\MessageUtil; | ||
use DOMDocument; | ||
use function file_put_contents; | ||
use function json_encode; | ||
use function libxml_use_internal_errors; | ||
use function preg_match; | ||
use function trim; | ||
use function usleep; | ||
use const JSON_PRETTY_PRINT; | ||
use const JSON_UNESCAPED_SLASHES; | ||
use const JSON_UNESCAPED_UNICODE; | ||
use const XML_ELEMENT_NODE; | ||
|
||
/** | ||
* @var \Psr\Http\Client\ClientInterface $http | ||
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory | ||
* @var \Psr\Log\LoggerInterface $logger | ||
* @var string $file | ||
*/ | ||
require_once __DIR__.'/spotify-common.php'; | ||
|
||
$file ??= __DIR__.'/mixesdb-data.json'; | ||
$baseURL = 'https://www.mixesdb.com'; | ||
$catPath = '/db/index.php?title=Category:Clubnight'; | ||
$tracklist = []; | ||
|
||
// suppress html parse errors | ||
libxml_use_internal_errors(true); | ||
|
||
do{ | ||
$logger->info($catPath); | ||
|
||
// fetch the category page | ||
$catRequest = $requestFactory->createRequest('GET', $baseURL.$catPath); | ||
$catResponse = $http->sendRequest($catRequest); | ||
|
||
if($catResponse->getStatusCode() !== 200){ | ||
break; | ||
} | ||
|
||
$catDOM = new DOMDocument('1.0', 'UTF-8'); | ||
$catDOM->loadHTML(MessageUtil::getContents($catResponse)); | ||
|
||
// get the pages from the category list | ||
foreach($catDOM->getElementById('catMixesList')->childNodes as $node){ | ||
|
||
if($node->nodeType !== XML_ELEMENT_NODE){ | ||
continue; | ||
} | ||
|
||
$page = $node->childNodes[0]->attributes->getNamedItem('href')->nodeValue; | ||
|
||
// get the date string | ||
preg_match('#\d{4}-\d{2}-\d{2}#', $page, $match); | ||
|
||
if(!isset($match[0])){ | ||
continue; | ||
} | ||
|
||
// fetch the page | ||
$pageRequest = $requestFactory->createRequest('GET', $baseURL.$page); | ||
$pageResponse = $http->sendRequest($pageRequest); | ||
|
||
if($pageResponse->getStatusCode() !== 200){ | ||
continue; | ||
} | ||
|
||
$pageDOM = new DOMDocument('1.0', 'UTF-8'); | ||
$pageDOM->loadHTML(MessageUtil::getContents($pageResponse)); | ||
|
||
$name = $pageDOM->getElementById('firstHeading')->nodeValue; | ||
|
||
$logger->info($name); | ||
|
||
// get the tracklist | ||
foreach($pageDOM->getElementsByTagName('ol') as $li){ | ||
foreach($li->childNodes as $e){ | ||
$tracklist[$match[0]][$name][] = trim($e->nodeValue); | ||
} | ||
} | ||
|
||
// try not to hammer | ||
usleep(500000); | ||
} | ||
|
||
// get the next page from the category navigation | ||
$catPath = null; | ||
|
||
foreach($catDOM->getElementById('catcount')->getElementsByTagName('a') as $node){ | ||
if($node->textContent === 'next 200'){ | ||
$catPath = $node->attributes->getNamedItem('href')->nodeValue; | ||
|
||
break; | ||
} | ||
} | ||
|
||
} | ||
while(!empty($catPath)); | ||
|
||
file_put_contents($file, json_encode($tracklist, (JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE))); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
/** | ||
* search-tracks.php | ||
* | ||
* @see https://de.wikipedia.org/wiki/Hr3_Clubnight | ||
* @see https://www.fr.de/kultur/letzten-rille-11671177.html | ||
* | ||
* @created 26.03.2023 | ||
* @author smiley <[email protected]> | ||
* @copyright 2023 smiley | ||
* @license MIT | ||
*/ | ||
|
||
namespace chillerlan\OAuthExamples\Providers\Spotify; | ||
|
||
use function file_exists; | ||
use function strtotime; | ||
|
||
/** | ||
* @var \chillerlan\OAuth\Providers\Spotify $spotify | ||
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory | ||
* @var \Psr\Log\LoggerInterface $logger | ||
* @var string $CFGDIR | ||
*/ | ||
require_once __DIR__.'/spotify-common.php'; | ||
|
||
$file = __DIR__.'/clubnights.json'; | ||
$since = strtotime('1990-05-05'); // first clubnight: 1990-05-05 | ||
$until = strtotime('2000-01-01'); // last clubnight: 2014-06-07 (studio), 2014-06-14 (live) | ||
$find = ['Dag', 'Fenslau', 'Pascal' /* F.E.O.S. */, 'Talla', 'Taucher', 'Tom Wax', 'Ulli Brenner', 'Väth']; | ||
$limit = 5; | ||
$playlistPerSet = false; | ||
|
||
if(!file_exists($file)){ | ||
include __DIR__.'/mixesdb-scrape.php'; | ||
} | ||
|
||
$client = new MixesDBTrackSearch($spotify, $logger); | ||
|
||
$client->getTracks($file, $since, $until, $find, $limit, $playlistPerSet); | ||
|
||
exit; |