diff --git a/.gitignore b/.gitignore index a89948e..7885edd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ config/systemd/que.service.erb script/deploy tmp/* public/media +*.p8 diff --git a/Gemfile b/Gemfile index 7f26e54..a5b112e 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,7 @@ gem "ruby-filemagic", git: "https://github.com/dnitza/ruby-filemagic", branch: " gem "webmention" gem "sanitize" gem "time_math2", require: "time_math" +gem "jwt" gem "lastfm", "~> 1.27" gem "mail" gem "que" diff --git a/Gemfile.lock b/Gemfile.lock index bba41b8..38f1924 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -286,6 +286,7 @@ GEM link-header-parser (~> 5.0) nokogiri (>= 1.13) json (2.6.3) + jwt (2.7.1) language_server-protocol (3.17.0.3) lastfm (1.27.4) httparty @@ -532,6 +533,7 @@ DEPENDENCIES hanami-view! httparty image_processing (~> 1.0) + jwt lastfm (~> 1.27) mail matrix diff --git a/app/actions/recently_played/index.rb b/app/actions/recently_played/index.rb new file mode 100644 index 0000000..24d3437 --- /dev/null +++ b/app/actions/recently_played/index.rb @@ -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 diff --git a/app/content/pages/now.md b/app/content/pages/now.md index 1022603..9b5cbd6 100644 --- a/app/content/pages/now.md +++ b/app/content/pages/now.md @@ -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). +### Currently listening to + +
+ ### 2023 Goals - 25 Hikes diff --git a/app/queries/posts/recently_played.rb b/app/queries/posts/recently_played.rb new file mode 100644 index 0000000..4e41a6c --- /dev/null +++ b/app/queries/posts/recently_played.rb @@ -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 diff --git a/app/templates/recently_played/index.html.slim b/app/templates/recently_played/index.html.slim new file mode 100644 index 0000000..74ef5c2 --- /dev/null +++ b/app/templates/recently_played/index.html.slim @@ -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] \ No newline at end of file diff --git a/app/views/recently_played/index.rb b/app/views/recently_played/index.rb new file mode 100644 index 0000000..113f9cc --- /dev/null +++ b/app/views/recently_played/index.rb @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 952828c..1d35b4a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -49,6 +49,8 @@ module Adamantium get "/movies", to: "movies.index" + get "/recently_played", to: "recently_played.index" + get "/:slug", to: "pages.show" get "/trips", to: "trips.index" diff --git a/config/settings.rb b/config/settings.rb index dce8029..bbc28f7 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -28,6 +28,10 @@ module Adamantium setting :lastfm_api_key, 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 :shortcut_key, default: nil diff --git a/slices/admin/actions/apple_music/index.rb b/slices/admin/actions/apple_music/index.rb new file mode 100644 index 0000000..dcdd511 --- /dev/null +++ b/slices/admin/actions/apple_music/index.rb @@ -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 diff --git a/slices/admin/config/routes.rb b/slices/admin/config/routes.rb index a6f24ed..0af80aa 100644 --- a/slices/admin/config/routes.rb +++ b/slices/admin/config/routes.rb @@ -49,5 +49,7 @@ module Admin post "/trips/remove_post", to: Auth.call(action: "trips.remove_post") get "/trips/new", to: Auth.call(action: "trips.new") post "/trips/:id", to: Auth.call(action: "trips.update") + + get "/apple_music", to: Auth.call(action: "apple_music.index") end end diff --git a/slices/admin/templates/apple_music/index.html.slim b/slices/admin/templates/apple_music/index.html.slim new file mode 100644 index 0000000..7922087 --- /dev/null +++ b/slices/admin/templates/apple_music/index.html.slim @@ -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 \ No newline at end of file diff --git a/slices/admin/templates/index.html.slim b/slices/admin/templates/index.html.slim index 689a6a9..526fe15 100644 --- a/slices/admin/templates/index.html.slim +++ b/slices/admin/templates/index.html.slim @@ -17,6 +17,8 @@ div class="max-w-prose mx-auto prose dark:prose-invert" a href="/admin/bookmarks" Bookmarks li 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" diff --git a/slices/admin/views/apple_music/index.rb b/slices/admin/views/apple_music/index.rb new file mode 100644 index 0000000..54b34a1 --- /dev/null +++ b/slices/admin/views/apple_music/index.rb @@ -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