From 2e3af5bb0fa93d0fc22adcd13d487af350720f61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Mon, 22 Aug 2022 16:35:03 +0200
Subject: [PATCH] Get top artists from API

---
 assets/app.js                                |  48 ++++++
 src/Controller/ArtistController.php          |  51 ++++++
 src/Controller/PlaylistController.php        |   9 --
 templates/playlist-generator/index.html.twig | 161 ++++++++-----------
 4 files changed, 169 insertions(+), 100 deletions(-)
 create mode 100644 src/Controller/ArtistController.php

diff --git a/assets/app.js b/assets/app.js
index b054960..3d98862 100644
--- a/assets/app.js
+++ b/assets/app.js
@@ -62,4 +62,52 @@ window.getPlaylists = function () {
     }
 };
 
+/* get artists and update form */
+window.getTopArtists = function () {
+    var containerElement = document.getElementById("user-top-artists");
+    if (containerElement) {
+        var htmlContent = "";
+
+        var apiUrl = "artists/top";
+        fetch(apiUrl)
+            .then(function (response) {
+                return response.json();
+            })
+            .then(function (data) {
+                const htmlTemplate = `
+                  <li class="col">
+                    <div class="custom-control custom-checkbox">
+                      <input
+                        type="checkbox"
+                        class="custom-control-input checkbox-artist"
+                        id="check-top-artist-ARTIST_ID"
+                        name="selected-artist[]"
+                        value="ARTIST_ID"
+                      >
+                      <label class="custom-control-label" for="check-top-artist-ARTIST_ID">
+                        <span class="spotify-image"><img src="ARTIST_IMAGE_URL" style="max-height: 1rem; max-width: 1rem;" /></span>
+                        ARTIST_NAME
+                      </label>
+                    </div>
+                  </li>
+                `;
+
+                data.forEach((artist) => {
+                    htmlContent += htmlTemplate
+                        .replace(/ARTIST_ID/g, artist["id"])
+                        .replace(/ARTIST_NAME/g, artist["name"])
+                        .replace(/ARTIST_IMAGE_URL/g, artist["imageUrl"]);
+                });
+                containerElement.innerHTML = htmlContent;
+            })
+            .catch(function (err) {
+                console.warn("Something went wrong.", err);
+                htmlContent =
+                    '<div class="alert alert-warning">Error fetching API data.</div>';
+                containerElement.innerHTML = htmlContent;
+            });
+    }
+};
+
 getPlaylists();
+getTopArtists();
diff --git a/src/Controller/ArtistController.php b/src/Controller/ArtistController.php
new file mode 100644
index 0000000..4acdd76
--- /dev/null
+++ b/src/Controller/ArtistController.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Controller;
+
+use App\Service\SpotifyApiService;
+use App\Service\SpotifyPlaylistService;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Annotation\Route;
+
+class ArtistController extends AbstractController
+{
+    /** @var SpotifyWebAPI */
+    private $spotifyApi;
+
+    public function __construct(SpotifyApiService $spotifyApiService)
+    {
+        // increase timeout limit
+        set_time_limit(60);
+
+        $this->spotifyApi = $spotifyApiService->createApi();
+    }
+
+    /**
+     * @Route("/artists/top", name="get-artists-top")
+     *
+     * @return Response
+     */
+    public function getArtists(): Response
+    {
+        $rawTopArtists = $this->spotifyApi->getMyTop(
+            'artists',
+            [
+                'limit' => SpotifyPlaylistService::TOP_ARTISTS_COUNT,
+                'time_range' => 'short_term'
+            ]
+        )->items;
+
+        $artists = [];
+        foreach ($rawTopArtists as $artist) {
+            $artists[] = [
+                'id' => $artist->id,
+                'name' => $artist->name,
+                'imageUrl' => $artist->images[\count($artist->images) - 1]->url,
+            ];
+        }
+
+        return new JsonResponse($artists);
+    }
+}
diff --git a/src/Controller/PlaylistController.php b/src/Controller/PlaylistController.php
index d5cce8d..6bfd4fd 100644
--- a/src/Controller/PlaylistController.php
+++ b/src/Controller/PlaylistController.php
@@ -37,17 +37,8 @@ class PlaylistController extends AbstractController
     {
         $me = $this->spotifyApi->me();
 
-        $topArtists = $this->spotifyApi->getMyTop(
-            'artists',
-            [
-                'limit' => SpotifyPlaylistService::TOP_ARTISTS_COUNT,
-                'time_range' => 'short_term'
-            ]
-        )->items;
-
         return $this->render('playlist-generator/index.html.twig', [
             'user' => $me,
-            'topArtists' => $topArtists,
         ]);
     }
 
diff --git a/templates/playlist-generator/index.html.twig b/templates/playlist-generator/index.html.twig
index d15064b..90fe0c5 100644
--- a/templates/playlist-generator/index.html.twig
+++ b/templates/playlist-generator/index.html.twig
@@ -4,108 +4,87 @@
 
 {% block content %}
 
-    {% if topArtists is defined %}
-      <form class="clearfix mt-2" action="{{ path('generate-quick-playlist') }}">
-        <div class="row">
-          <div class="col-md-12">
-            <legend>Quick playlist generator:</legend>
+    <form class="clearfix mt-2" action="{{ path('generate-quick-playlist') }}">
+      <div class="row">
+        <div class="col-md-12">
+          <legend>Quick playlist generator:</legend>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 col-sm-12">
+          <div>
+            <input
+              type="checkbox"
+              class="custom-control-input"
+              id="check-quick-create-long-playlist"
+              name="check-quick-create-long-playlist"
+              value="long-playlist"
+              checked
+            >
+            <label class="custom-control-label" for="check-quick-create-long-playlist">
+              Generate a long playlist (x2)
+            </label>
           </div>
         </div>
-        <div class="row">
-          <div class="col-md-6 col-sm-12">
-            <div>
-              <input
-                type="checkbox"
-                class="custom-control-input"
-                id="check-quick-create-long-playlist"
-                name="check-quick-create-long-playlist"
-                value="long-playlist"
-                checked
-              >
-              <label class="custom-control-label" for="check-quick-create-long-playlist">
-                Generate a long playlist (x2)
-              </label>
-            </div>
+        <div class="col-md-6 col-sm-12">
+          <div class="btn-group float-end" role="group" aria-label="Action buttons">
+            <button name="generate-quick-playlist-from-daily-mixes" type="submit" class="btn btn-secondary">🎶 Mix daily mixes!</button>
+            <button name="generate-quick-playlist-tambouille-mix" type="submit" class="btn btn-primary">🎶 Pick nice tracks!</button>
           </div>
-          <div class="col-md-6 col-sm-12">
-            <div class="btn-group float-end" role="group" aria-label="Action buttons">
-              <button name="generate-quick-playlist-from-daily-mixes" type="submit" class="btn btn-secondary">🎶 Mix daily mixes!</button>
-              <button name="generate-quick-playlist-tambouille-mix" type="submit" class="btn btn-primary">🎶 Pick nice tracks!</button>
-            </div>
+        </div>
+      </div>
+    </form>
+
+    <form class="clearfix mt-2" action="{{ path('generate-playlist-from-top-artists') }}">
+      <div class="row">
+        <div class="col-md-8 col-sm-12">
+          <legend>Generate playlist from artists:</legend>
+        </div>
+        <div class="col-md-4 col-sm-12">
+          <div class="btn-group float-end" role="group" aria-label="Action buttons">
+            <button name="pick-random-artist" type="button" class="btn btn-link" onclick="window.pickRandomArtists()">🎲 Pick random artists</button>
           </div>
         </div>
-      </form>
+      </div>
+      <ul class="list-unstyled row row-cols-md-4 row-cols-2" id="user-top-artists">
 
-      <form class="clearfix mt-2" action="{{ path('generate-playlist-from-top-artists') }}">
-        <div class="row">
-          <div class="col-md-8 col-sm-12">
-            <legend>Generate playlist from artists:</legend>
+      </ul>
+      <div class="row">
+        <div class="col-md-8 col-sm-12">
+          <div>
+            <input
+              type="checkbox"
+              class="custom-control-input"
+              id="check-top-artist-filter-artists"
+              name="check-top-artist-filter-artists"
+              value="filter-artists"
+              checked
+            >
+            <label class="custom-control-label" for="check-top-artist-filter-artists">
+              Allow only selected artists in recommendations
+            </label>
           </div>
-          <div class="col-md-4 col-sm-12">
-            <div class="btn-group float-end" role="group" aria-label="Action buttons">
-              <button name="pick-random-artist" type="button" class="btn btn-link" onclick="window.pickRandomArtists()">🎲 Pick random artists</button>
-            </div>
+          <div>
+            <input
+              type="checkbox"
+              class="custom-control-input"
+              id="check-top-artist-long-playlist"
+              name="check-top-artist-long-playlist"
+              value="long-playlist"
+            >
+            <label class="custom-control-label" for="check-top-artist-long-playlist">
+              Generate a long playlist (x2)
+            </label>
           </div>
         </div>
-        <ul class="list-unstyled row row-cols-md-4 row-cols-2">
-          {% for artist in topArtists %}
-            <li class="col">
-              <div class="custom-control custom-checkbox">
-                <input
-                  type="checkbox"
-                  class="custom-control-input checkbox-artist"
-                  id="check-top-artist-{{ artist.id }}"
-                  name="selected-artist[]"
-                  value="{{ artist.id }}"
-                >
-                <label class="custom-control-label" for="check-top-artist-{{ artist.id }}">
-                  {% if artist.images is defined %}
-                  {% set artistLastImage = artist.images|last %}
-                    <span class="spotify-image"><img src="{{ artistLastImage.url }}" style="max-height: 1rem; max-width: 1rem;" /></span>
-                  {% endif %}
-                  {{ artist.name }}
-                </label>
-              </div>
-            </li>
-          {% endfor %}
-        </ul>
-        <div class="row">
-          <div class="col-md-8 col-sm-12">
-            <div>
-              <input
-                type="checkbox"
-                class="custom-control-input"
-                id="check-top-artist-filter-artists"
-                name="check-top-artist-filter-artists"
-                value="filter-artists"
-                checked
-              >
-              <label class="custom-control-label" for="check-top-artist-filter-artists">
-                Allow only selected artists in recommendations
-              </label>
-            </div>
-            <div>
-              <input
-                type="checkbox"
-                class="custom-control-input"
-                id="check-top-artist-long-playlist"
-                name="check-top-artist-long-playlist"
-                value="long-playlist"
-              >
-              <label class="custom-control-label" for="check-top-artist-long-playlist">
-                Generate a long playlist (x2)
-              </label>
-            </div>
-          </div>
-          <div class="col-md-4 col-sm-12">
-            <div class="btn-group float-end" role="group" aria-label="Action buttons">
-              <button name="generate-playlist-random-top-artists" type="submit" class="btn btn-secondary">🎲 Random!</button>
-              <button name="generate-playlist-top-artists" type="submit" class="btn btn-primary">🎶 Generate!</button>
-            </div>
+        <div class="col-md-4 col-sm-12">
+          <div class="btn-group float-end" role="group" aria-label="Action buttons">
+            <button name="generate-playlist-random-top-artists" type="submit" class="btn btn-secondary">🎲 Random!</button>
+            <button name="generate-playlist-top-artists" type="submit" class="btn btn-primary">🎶 Generate!</button>
           </div>
         </div>
-      </form>
-    {% endif %}
+      </div>
+    </form>
 
     <form class="clearfix" action="{{ path('generate-playlist-from-playlists') }}">
       <legend>Generate playlist from tracks in existing playlist:</legend>
-- 
GitLab