diff --git a/app.js b/app.js index 5ea8eb4db..e66c3131a 100644 --- a/app.js +++ b/app.js @@ -2,8 +2,7 @@ require('dotenv').config(); const express = require('express'); const hbs = require('hbs'); - -// require spotify-web-api-node package here: +const SpotifyWebApi = require('spotify-web-api-node'); const app = express(); @@ -11,8 +10,64 @@ app.set('view engine', 'hbs'); app.set('views', __dirname + '/views'); app.use(express.static(__dirname + '/public')); -// setting the spotify-api goes here: +// Spotify API setup +const spotifyApi = new SpotifyWebApi({ + clientId: process.env.CLIENT_ID, + clientSecret: process.env.CLIENT_SECRET +}); + +// Retrieve an access token +spotifyApi + .clientCredentialsGrant() + .then(data => spotifyApi.setAccessToken(data.body['access_token'])) + .catch(error => console.log('Something went wrong when retrieving an access token', error)); + +// Home route +app.get('/', (req, res) => { + res.render('index'); +}); + +// Artist search route +app.get('/artist-search', (req, res) => { + const { artist } = req.query; + + spotifyApi + .searchArtists(artist) + .then(data => { + console.log('The received data from the API: ', data.body); + res.render('artist-search-results', { artists: data.body.artists.items }); + }) + .catch(err => console.log('The error while searching artists occurred: ', err)); +}); + +// Albums route +app.get('/albums/:artistId', (req, res) => { + const { artistId } = req.params; + + spotifyApi + .getArtistAlbums(artistId) + .then(data => { + console.log('Artist albums', data.body); + spotifyApi.getArtist(artistId).then(artistData => { + res.render('albums', { albums: data.body.items, artistName: artistData.body.name }); + }); + }) + .catch(err => console.log('The error while searching albums occurred: ', err)); +}); + +// Tracks route +app.get('/tracks/:albumId', (req, res) => { + const { albumId } = req.params; -// Our routes go here: + spotifyApi + .getAlbumTracks(albumId) + .then(data => { + console.log('Album tracks', data.body); + spotifyApi.getAlbum(albumId).then(albumData => { + res.render('tracks', { tracks: data.body.items, albumName: albumData.body.name }); + }); + }) + .catch(err => console.log('The error while searching tracks occurred: ', err)); +}); app.listen(3000, () => console.log('My Spotify project running on port 3000 🎧 🥁 🎸 🔊')); diff --git a/package.json b/package.json index c9f4085ba..bed238e08 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,12 @@ "author": "", "license": "ISC", "devDependencies": { - "nodemon": "^2.0.2" + "nodemon": "^2.0.22" + }, + "dependencies": { + "dotenv": "^16.4.5", + "express": "^4.19.2", + "hbs": "^4.2.0", + "spotify-web-api-node": "^5.0.2" } } diff --git a/public/styles/style.css b/public/styles/style.css index e69de29bb..c74e92f3c 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -0,0 +1,162 @@ +body, html { + height: 100%; + margin: 0; + font-family: Arial, Helvetica, sans-serif; + } + + .background { + background-image: url('/images/spotify-background.jpeg'); + height: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + display: flex; + justify-content: center; + align-items: center; + } + + .search-container { + background-color: rgba(255, 255, 255, 0.8); /* 透明度のある背景 */ + padding: 20px; + border-radius: 10px; + } + + .search-container input[type="text"] { + padding: 10px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 5px; + } + + .search-container button { + padding: 10px 20px; + background-color: #f44336; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + } + + .search-container button:hover { + background-color: #d32f2f; + } + + h1 { + text-align: center; + margin-top: 20px; + } + + .artists-container, .albums-container { + display: flex; + justify-content: center; + flex-wrap: wrap; + margin-top: 20px; + } + + .artist, .album { + display: flex; + flex-direction: column; + justify-content: space-between; + margin: 20px; + border: 1px solid #ddd; + border-radius: 5px; + overflow: hidden; + width: 200px; + height: 400px; /* 固定の高さを設定 */ + text-align: center; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + .artist img, .album img, .artist .no-image, .album .no-image { + width: 100%; + height: auto; + max-height: 300px; /* 画像の最大高さを設定 */ + } + + .artist .no-image, .album .no-image { + display: flex; + justify-content: center; + align-items: center; + height: 300px; /* 画像がない場合の高さを設定 */ + background-color: #f0f0f0; + color: #888; + } + + .artist h2, .album h2 { + font-size: 16px; + margin: 10px 0; + } + + .btn { + display: block; + padding: 10px 20px; + background-color: #f44336; + color: white; + text-decoration: none; + border-radius: 5px; + margin: 10px auto 0; + transition: background-color 0.3s; + } + + .btn:hover { + background-color: #d32f2f; + } + + .tracks-table { + width: 80%; + margin: 20px auto; + border-collapse: collapse; + } + + .tracks-table th, .tracks-table td { + border: 1px solid #ddd; + padding: 10px; + } + + .tracks-table .title-header { + text-align: left; + } + + .tracks-table .listen-header { + text-align: center; + } + + .tracks-table .title-cell { + text-align: left; + } + + .tracks-table .listen-cell { + text-align: right; + } + + .tracks-table th { + background-color: #333; + color: white; + } + + .tracks-table td { + background-color: #f9f9f9; + } + + .audio-container { + display: flex; + justify-content: flex-end; + align-items: center; + } + + .audio-container audio { + margin-right: 10px; + } + + .download-btn { + color: #f44336; + text-decoration: none; + padding: 5px; + border-radius: 5px; + transition: color 0.3s; + } + + .download-btn:hover { + color: #d32f2f; + } + \ No newline at end of file diff --git a/views/albums.hbs b/views/albums.hbs new file mode 100644 index 000000000..6738c2aa2 --- /dev/null +++ b/views/albums.hbs @@ -0,0 +1,27 @@ + + +
+ + +Title | +Listen | +
---|---|
{{this.name}} | +
+ {{#if this.preview_url}}
+
+
+
+
+
+
+ {{else}}
+ No preview available
+ {{/if}}
+ |
+