Add Apple Music integration for now playing
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,3 +21,4 @@ config/systemd/que.service.erb
|
|||||||
script/deploy
|
script/deploy
|
||||||
tmp/*
|
tmp/*
|
||||||
public/media
|
public/media
|
||||||
|
*.p8
|
||||||
|
1
Gemfile
1
Gemfile
@@ -42,6 +42,7 @@ gem "ruby-filemagic", git: "https://github.com/dnitza/ruby-filemagic", branch: "
|
|||||||
gem "webmention"
|
gem "webmention"
|
||||||
gem "sanitize"
|
gem "sanitize"
|
||||||
gem "time_math2", require: "time_math"
|
gem "time_math2", require: "time_math"
|
||||||
|
gem "jwt"
|
||||||
gem "lastfm", "~> 1.27"
|
gem "lastfm", "~> 1.27"
|
||||||
gem "mail"
|
gem "mail"
|
||||||
gem "que"
|
gem "que"
|
||||||
|
@@ -286,6 +286,7 @@ GEM
|
|||||||
link-header-parser (~> 5.0)
|
link-header-parser (~> 5.0)
|
||||||
nokogiri (>= 1.13)
|
nokogiri (>= 1.13)
|
||||||
json (2.6.3)
|
json (2.6.3)
|
||||||
|
jwt (2.7.1)
|
||||||
language_server-protocol (3.17.0.3)
|
language_server-protocol (3.17.0.3)
|
||||||
lastfm (1.27.4)
|
lastfm (1.27.4)
|
||||||
httparty
|
httparty
|
||||||
@@ -532,6 +533,7 @@ DEPENDENCIES
|
|||||||
hanami-view!
|
hanami-view!
|
||||||
httparty
|
httparty
|
||||||
image_processing (~> 1.0)
|
image_processing (~> 1.0)
|
||||||
|
jwt
|
||||||
lastfm (~> 1.27)
|
lastfm (~> 1.27)
|
||||||
mail
|
mail
|
||||||
matrix
|
matrix
|
||||||
|
12
app/actions/recently_played/index.rb
Normal file
12
app/actions/recently_played/index.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module Adamantium
|
||||||
|
module Actions
|
||||||
|
module RecentlyPlayed
|
||||||
|
class Index < Action
|
||||||
|
include Deps["views.recently_played.index"]
|
||||||
|
def handle(req, res)
|
||||||
|
res.render index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -27,6 +27,10 @@ I currently live in Canberra with my partner and [our dogs](https://instagram.co
|
|||||||
|
|
||||||
In my spare time I like to tinker on various Ruby projects (including the software that powers this blog), make things with [Processing](https://processing.org), explore the various [hiking trails](/hikes) around Canberra and potter around [in the garden](/tagged/garden).
|
In my spare time I like to tinker on various Ruby projects (including the software that powers this blog), make things with [Processing](https://processing.org), explore the various [hiking trails](/hikes) around Canberra and potter around [in the garden](/tagged/garden).
|
||||||
|
|
||||||
|
### Currently listening to
|
||||||
|
|
||||||
|
<div hx-get="/recently_played" hx-trigger="load"></div>
|
||||||
|
|
||||||
### 2023 Goals
|
### 2023 Goals
|
||||||
|
|
||||||
- 25 Hikes
|
- 25 Hikes
|
||||||
|
43
app/queries/posts/recently_played.rb
Normal file
43
app/queries/posts/recently_played.rb
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
require "httparty"
|
||||||
|
require "jwt"
|
||||||
|
|
||||||
|
module Adamantium
|
||||||
|
module Queries
|
||||||
|
module Posts
|
||||||
|
class RecentlyPlayed
|
||||||
|
include Deps["settings"]
|
||||||
|
|
||||||
|
def call
|
||||||
|
resp = HTTParty.get("https://api.music.apple.com/v1/me/recent/played", {
|
||||||
|
headers: {
|
||||||
|
"Authorization" => "Bearer #{jwt}",
|
||||||
|
"Music-User-Token" => settings.apple_music_user_token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
resp.body
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def jwt
|
||||||
|
authentication_payload = {
|
||||||
|
iss: settings.apple_music_team,
|
||||||
|
iat: Time.now.to_i, # Issue date
|
||||||
|
exp: Time.now.to_i + 3600 # Expiry of this token.
|
||||||
|
}
|
||||||
|
# The file we got from Apple
|
||||||
|
apple_music_secret = File.read(File.join(Hanami.app.root, "config", "AuthKey_#{settings.apple_music_key}.p8"))
|
||||||
|
private_key = OpenSSL::PKey::EC.new(apple_music_secret)
|
||||||
|
|
||||||
|
JWT.encode(
|
||||||
|
authentication_payload,
|
||||||
|
private_key,
|
||||||
|
'ES256',
|
||||||
|
kid: settings.apple_music_key
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
app/templates/recently_played/index.html.slim
Normal file
9
app/templates/recently_played/index.html.slim
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
div class="grid grid-cols-4 gap-4"
|
||||||
|
- recently_played_music.each do |album|
|
||||||
|
a href="#{album[:href]}"
|
||||||
|
div
|
||||||
|
img class="rounded transition-transform ease-out hover:scale-105" src="#{album[:image]}"
|
||||||
|
span class="inline-block text-sm"
|
||||||
|
= album[:name]
|
||||||
|
span class="inline-block text-sm font-bold"
|
||||||
|
= album[:artist]
|
27
app/views/recently_played/index.rb
Normal file
27
app/views/recently_played/index.rb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
module Adamantium
|
||||||
|
module Views
|
||||||
|
module RecentlyPlayed
|
||||||
|
class Index < Adamantium::View
|
||||||
|
config.layout = false
|
||||||
|
|
||||||
|
include Deps["queries.posts.recently_played"]
|
||||||
|
|
||||||
|
expose :recently_played_music do |recently_played_result|
|
||||||
|
# raise recently_played_result["data"].inspect
|
||||||
|
JSON.parse(recently_played_result)["data"].reject{ |a| a["type"] != "albums" }.map do |album|
|
||||||
|
{
|
||||||
|
artist: album["attributes"]["artistName"],
|
||||||
|
name: album["attributes"]["name"],
|
||||||
|
image: album["attributes"]["artwork"]["url"].gsub("{w}", "256").gsub("{h}", "256"),
|
||||||
|
href: album["attributes"]["url"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private_expose :recently_played_result do
|
||||||
|
recently_played.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -49,6 +49,8 @@ module Adamantium
|
|||||||
|
|
||||||
get "/movies", to: "movies.index"
|
get "/movies", to: "movies.index"
|
||||||
|
|
||||||
|
get "/recently_played", to: "recently_played.index"
|
||||||
|
|
||||||
get "/:slug", to: "pages.show"
|
get "/:slug", to: "pages.show"
|
||||||
|
|
||||||
get "/trips", to: "trips.index"
|
get "/trips", to: "trips.index"
|
||||||
|
@@ -28,6 +28,10 @@ module Adamantium
|
|||||||
setting :lastfm_api_key, default: nil
|
setting :lastfm_api_key, default: nil
|
||||||
setting :lastfm_secret, default: nil
|
setting :lastfm_secret, default: nil
|
||||||
|
|
||||||
|
setting :apple_music_team, default: nil
|
||||||
|
setting :apple_music_key, default: nil
|
||||||
|
setting :apple_music_user_token, default: nil
|
||||||
|
|
||||||
setting :omdb_api_key, default: nil
|
setting :omdb_api_key, default: nil
|
||||||
|
|
||||||
setting :shortcut_key, default: nil
|
setting :shortcut_key, default: nil
|
||||||
|
13
slices/admin/actions/apple_music/index.rb
Normal file
13
slices/admin/actions/apple_music/index.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module Admin
|
||||||
|
module Actions
|
||||||
|
module AppleMusic
|
||||||
|
class Index < Action
|
||||||
|
include Deps["views.apple_music.index"]
|
||||||
|
|
||||||
|
def handle(req, res)
|
||||||
|
res.render index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -49,5 +49,7 @@ module Admin
|
|||||||
post "/trips/remove_post", to: Auth.call(action: "trips.remove_post")
|
post "/trips/remove_post", to: Auth.call(action: "trips.remove_post")
|
||||||
get "/trips/new", to: Auth.call(action: "trips.new")
|
get "/trips/new", to: Auth.call(action: "trips.new")
|
||||||
post "/trips/:id", to: Auth.call(action: "trips.update")
|
post "/trips/:id", to: Auth.call(action: "trips.update")
|
||||||
|
|
||||||
|
get "/apple_music", to: Auth.call(action: "apple_music.index")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
32
slices/admin/templates/apple_music/index.html.slim
Normal file
32
slices/admin/templates/apple_music/index.html.slim
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
script src="https://js-cdn.music.apple.com/musickit/v3/musickit.js" data-web-components async
|
||||||
|
|
||||||
|
javascript:
|
||||||
|
document.addEventListener('musickitloaded', async function () {
|
||||||
|
// Call configure() to configure an instance of MusicKit on the Web.
|
||||||
|
try {
|
||||||
|
await MusicKit.configure({
|
||||||
|
developerToken: '#{developer_token}',
|
||||||
|
app: {
|
||||||
|
name: 'Blog',
|
||||||
|
build: '2023.1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// Handle configuration error
|
||||||
|
}
|
||||||
|
|
||||||
|
// MusicKit instance is available
|
||||||
|
const music = MusicKit.getInstance();
|
||||||
|
await music.authorize();
|
||||||
|
|
||||||
|
document.querySelectorAll(".mut")[0].innerHTML = music.musicUserToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200"
|
||||||
|
h1 Admin // Apple Music auth
|
||||||
|
|
||||||
|
div
|
||||||
|
p Your music user token is:
|
||||||
|
code
|
||||||
|
pre class="mut"
|
||||||
|
p keep it secret, keep it safe
|
@@ -17,6 +17,8 @@ div class="max-w-prose mx-auto prose dark:prose-invert"
|
|||||||
a href="/admin/bookmarks" Bookmarks
|
a href="/admin/bookmarks" Bookmarks
|
||||||
li
|
li
|
||||||
a href="/admin/trips" Trips
|
a href="/admin/trips" Trips
|
||||||
|
li
|
||||||
|
a href="/admin/apple_music" Apple Music
|
||||||
|
|
||||||
div class="max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600"
|
div class="max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600"
|
||||||
|
|
||||||
|
29
slices/admin/views/apple_music/index.rb
Normal file
29
slices/admin/views/apple_music/index.rb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
require "jwt"
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
module Views
|
||||||
|
module AppleMusic
|
||||||
|
class Index < Admin::View
|
||||||
|
include Deps["settings"]
|
||||||
|
|
||||||
|
expose :developer_token do
|
||||||
|
authentication_payload = {
|
||||||
|
iss: settings.apple_music_team,
|
||||||
|
iat: Time.now.to_i, # Issue date
|
||||||
|
exp: Time.now.to_i + 3600 # Expiry of this token.
|
||||||
|
}
|
||||||
|
# The file we got from Apple
|
||||||
|
apple_music_secret = File.read(File.join(Hanami.app.root, "config", "AuthKey_#{settings.apple_music_key}.p8"))
|
||||||
|
private_key = OpenSSL::PKey::EC.new(apple_music_secret)
|
||||||
|
|
||||||
|
JWT.encode(
|
||||||
|
authentication_payload,
|
||||||
|
private_key,
|
||||||
|
'ES256',
|
||||||
|
kid: settings.apple_music_key
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user