Skip to content
This repository was archived by the owner on Mar 23, 2024. It is now read-only.

Commit 4d9dfb5

Browse files
committed
✨ +MixesDB example
1 parent 29b94f0 commit 4d9dfb5

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
/**
3+
* Class MixesDBTrackSearch
4+
*
5+
* @created 30.08.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\OAuthExamples\Providers\Spotify;
12+
13+
use chillerlan\HTTP\Utils\MessageUtil;
14+
use function array_column;
15+
use function array_map;
16+
use function array_merge;
17+
use function explode;
18+
use function file_get_contents;
19+
use function implode;
20+
use function json_decode;
21+
use function preg_replace;
22+
use function sprintf;
23+
use function str_replace;
24+
use function strtotime;
25+
use function trim;
26+
use function usleep;
27+
28+
/**
29+
*
30+
*/
31+
class MixesDBTrackSearch extends SpotifyClient{
32+
33+
/**
34+
* search tracks on spotify from the given mixesdb track lists
35+
*/
36+
public function getTracks(
37+
string $clubnightsJSON,
38+
int $since,
39+
int $until,
40+
array $find = [],
41+
int $limit = 5,
42+
bool $playlistPerSet = false,
43+
):void{
44+
$clubnights = json_decode(file_get_contents($clubnightsJSON), true);
45+
$tracks = [];
46+
47+
foreach($clubnights as $date => $sets){
48+
$date = strtotime($date);
49+
// skip by date
50+
if($date < $since || $date > $until){
51+
continue;
52+
}
53+
54+
foreach($sets as $name => $set){
55+
// skip by inclusion list
56+
if($this->setContains($name, $find)){
57+
continue;
58+
}
59+
60+
$this->logger->info($name);
61+
$setTracks = [];
62+
63+
foreach($set as $track){
64+
$track = $this->cleanTrack($track);
65+
66+
if(empty($track)){
67+
continue;
68+
}
69+
70+
$this->logger->info(sprintf('search: %s', $track));
71+
72+
$response = $this->spotify->request('/v1/search', [
73+
'q' => $this->getSearchTerm($track),
74+
'type' => 'track',
75+
'limit' => $limit,
76+
'market' => $this->market,
77+
]);
78+
79+
usleep(self::sleepTimer);
80+
81+
if($response->getStatusCode() !== 200){
82+
continue;
83+
}
84+
85+
$data = MessageUtil::decodeJSON($response);
86+
87+
foreach($data->tracks->items as $i => $item){
88+
$setTracks[$item->id] = $item->id;
89+
90+
$this->logger->info(sprintf('found: [%s][%s] %s - %s', ++$i, $item->id, implode(', ', array_column($item->artists, 'name')), $item->name));
91+
}
92+
93+
}
94+
95+
if($playlistPerSet){
96+
$playlistID = $this->createPlaylist($name, '');
97+
$this->addTracks($playlistID, $setTracks);
98+
}
99+
100+
$tracks = array_merge($tracks, $setTracks);
101+
}
102+
103+
}
104+
105+
$playlistID = $this->createPlaylist('mixesdb search result', '');
106+
$this->addTracks($playlistID, $tracks);
107+
}
108+
109+
/**
110+
* check a string for the occurence of any in the given array of needles
111+
*/
112+
protected function setContains(string $haystack, array $needles):bool{
113+
$haystack = mb_strtolower($haystack);
114+
115+
return !empty($needles) && str_replace(array_map('mb_strtolower', $needles), '', $haystack) === $haystack;
116+
}
117+
118+
/**
119+
* clean any unwanted symbols/strings from the track name
120+
*/
121+
protected function cleanTrack(string $track):string{
122+
// strip time codes [01:23] and record IDs [EYE Q - 001] from name
123+
return trim(preg_replace(['/^\[[\d:?]+\] /', '/ \[[^]]+\]/'], '', $track), ' -?');
124+
}
125+
126+
/**
127+
* prepare the spotify search term
128+
*/
129+
protected function getSearchTerm(string $track):string{
130+
$at = explode(' - ', $track, 2); // artist - track
131+
132+
return match (count($at)){
133+
1 => sprintf('artist:%1$s track:%1$s', $at[0]),
134+
2 => sprintf('artist:%s track:%s', $at[0], $at[1]),
135+
};
136+
}
137+
138+
}

examples/Providers/Spotify/SpotifyClient.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ protected function addTracks(string $playlistID, array $trackIDs):void{
247247
headers: ['Content-Type' => 'application/json'],
248248
);
249249

250+
usleep(self::sleepTimer);
251+
250252
if($playlistAddTracks->getStatusCode() === 201){
251253
$json = MessageUtil::decodeJSON($playlistAddTracks);
252254

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
/**
3+
* mixesdb-scrape.php
4+
*
5+
* Not an actual OAuth API example, just a helper script
6+
*
7+
* Unfortunately, mixesdb doesn't have the mediawiki API activated, so we gotta scrape the data manually.
8+
*
9+
* @see https://www.mixesdb.com/w/Category:Clubnight
10+
*
11+
* @created 28.03.2023
12+
* @author smiley <[email protected]>
13+
* @copyright 2023 smiley
14+
* @license MIT
15+
*
16+
* @noinspection PhpComposerExtensionStubsInspection
17+
*/
18+
19+
namespace chillerlan\OAuthExamples\Providers\Spotify;
20+
21+
use chillerlan\HTTP\Utils\MessageUtil;
22+
use DOMDocument;
23+
use function file_put_contents;
24+
use function json_encode;
25+
use function libxml_use_internal_errors;
26+
use function preg_match;
27+
use function trim;
28+
use function usleep;
29+
use const JSON_PRETTY_PRINT;
30+
use const JSON_UNESCAPED_SLASHES;
31+
use const JSON_UNESCAPED_UNICODE;
32+
use const XML_ELEMENT_NODE;
33+
34+
/**
35+
* @var \Psr\Http\Client\ClientInterface $http
36+
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory
37+
* @var \Psr\Log\LoggerInterface $logger
38+
* @var string $file
39+
*/
40+
require_once __DIR__.'/spotify-common.php';
41+
42+
$file ??= __DIR__.'/mixesdb-data.json';
43+
$baseURL = 'https://www.mixesdb.com';
44+
$catPath = '/db/index.php?title=Category:Clubnight';
45+
$tracklist = [];
46+
47+
// suppress html parse errors
48+
libxml_use_internal_errors(true);
49+
50+
do{
51+
$logger->info($catPath);
52+
53+
// fetch the category page
54+
$catRequest = $requestFactory->createRequest('GET', $baseURL.$catPath);
55+
$catResponse = $http->sendRequest($catRequest);
56+
57+
if($catResponse->getStatusCode() !== 200){
58+
break;
59+
}
60+
61+
$catDOM = new DOMDocument('1.0', 'UTF-8');
62+
$catDOM->loadHTML(MessageUtil::getContents($catResponse));
63+
64+
// get the pages from the category list
65+
foreach($catDOM->getElementById('catMixesList')->childNodes as $node){
66+
67+
if($node->nodeType !== XML_ELEMENT_NODE){
68+
continue;
69+
}
70+
71+
$page = $node->childNodes[0]->attributes->getNamedItem('href')->nodeValue;
72+
73+
// get the date string
74+
preg_match('#\d{4}-\d{2}-\d{2}#', $page, $match);
75+
76+
if(!isset($match[0])){
77+
continue;
78+
}
79+
80+
// fetch the page
81+
$pageRequest = $requestFactory->createRequest('GET', $baseURL.$page);
82+
$pageResponse = $http->sendRequest($pageRequest);
83+
84+
if($pageResponse->getStatusCode() !== 200){
85+
continue;
86+
}
87+
88+
$pageDOM = new DOMDocument('1.0', 'UTF-8');
89+
$pageDOM->loadHTML(MessageUtil::getContents($pageResponse));
90+
91+
$name = $pageDOM->getElementById('firstHeading')->nodeValue;
92+
93+
$logger->info($name);
94+
95+
// get the tracklist
96+
foreach($pageDOM->getElementsByTagName('ol') as $li){
97+
foreach($li->childNodes as $e){
98+
$tracklist[$match[0]][$name][] = trim($e->nodeValue);
99+
}
100+
}
101+
102+
// try not to hammer
103+
usleep(500000);
104+
}
105+
106+
// get the next page from the category navigation
107+
$catPath = null;
108+
109+
foreach($catDOM->getElementById('catcount')->getElementsByTagName('a') as $node){
110+
if($node->textContent === 'next 200'){
111+
$catPath = $node->attributes->getNamedItem('href')->nodeValue;
112+
113+
break;
114+
}
115+
}
116+
117+
}
118+
while(!empty($catPath));
119+
120+
file_put_contents($file, json_encode($tracklist, (JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)));
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* search-tracks.php
4+
*
5+
* @see https://de.wikipedia.org/wiki/Hr3_Clubnight
6+
* @see https://www.fr.de/kultur/letzten-rille-11671177.html
7+
*
8+
* @created 26.03.2023
9+
* @author smiley <[email protected]>
10+
* @copyright 2023 smiley
11+
* @license MIT
12+
*/
13+
14+
namespace chillerlan\OAuthExamples\Providers\Spotify;
15+
16+
use function file_exists;
17+
use function strtotime;
18+
19+
/**
20+
* @var \chillerlan\OAuth\Providers\Spotify $spotify
21+
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory
22+
* @var \Psr\Log\LoggerInterface $logger
23+
* @var string $CFGDIR
24+
*/
25+
require_once __DIR__.'/spotify-common.php';
26+
27+
$file = __DIR__.'/clubnights.json';
28+
$since = strtotime('1990-05-05'); // first clubnight: 1990-05-05
29+
$until = strtotime('2000-01-01'); // last clubnight: 2014-06-07 (studio), 2014-06-14 (live)
30+
$find = ['Dag', 'Fenslau', 'Pascal' /* F.E.O.S. */, 'Talla', 'Taucher', 'Tom Wax', 'Ulli Brenner', 'Väth'];
31+
$limit = 5;
32+
$playlistPerSet = false;
33+
34+
if(!file_exists($file)){
35+
include __DIR__.'/mixesdb-scrape.php';
36+
}
37+
38+
$client = new MixesDBTrackSearch($spotify, $logger);
39+
40+
$client->getTracks($file, $since, $until, $find, $limit, $playlistPerSet);
41+
42+
exit;

0 commit comments

Comments
 (0)