From 24607672c975523d57e6d883fabcfa699cb0f3c4 Mon Sep 17 00:00:00 2001 From: sHa Date: Fri, 5 Apr 2024 14:52:13 +0300 Subject: [PATCH] initial commit --- .env.example | 3 +++ .gitignore | 4 ++++ connectors/apple_music.py | 17 ++++++++++++++++ connectors/spotify.py | 17 ++++++++++++++++ mattermost.py | 43 +++++++++++++++++++++++++++++++++++++++ music.py | 9 ++++++++ music_app.py | 43 +++++++++++++++++++++++++++++++++++++++ readme.txt | 27 ++++++++++++++++++++++++ requirements.txt | 4 ++++ 9 files changed, 167 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 connectors/apple_music.py create mode 100644 connectors/spotify.py create mode 100644 mattermost.py create mode 100644 music.py create mode 100644 music_app.py create mode 100644 readme.txt create mode 100644 requirements.txt diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..943a47c --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +MATTERMOST_ACCESS_TOKEN= +MATTERMOST_SERVER_URL=https://my-mattermost.host +MUSIC_APP=apple_music # or spotify diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..33887f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +__pycache__ +.mypy_cache +.venv diff --git a/connectors/apple_music.py b/connectors/apple_music.py new file mode 100644 index 0000000..cdfefb8 --- /dev/null +++ b/connectors/apple_music.py @@ -0,0 +1,17 @@ +from appscript import app # type: ignore + +class AppleMusic: + def __init__(self): + self.music_app = app("Music") + + def get_current_track_info(self) -> tuple: + try: + current_track = self.music_app.current_track.get() + return ( + current_track.name.get(), + current_track.artist.get(), + current_track.duration.get(), + ) + except Exception as e: + print(f"Failed to get current track info: {e}") + return None, None, None diff --git a/connectors/spotify.py b/connectors/spotify.py new file mode 100644 index 0000000..9d93bda --- /dev/null +++ b/connectors/spotify.py @@ -0,0 +1,17 @@ +import osascript # type: ignore + +class Spotify: + def get_current_track_info(self) -> tuple: + try: + name_code = 'tell application "Spotify" to name of current track as string' + artist_code = 'tell application "Spotify" to artist of current track as string' + duration_code = 'tell application "Spotify" to duration of current track as string' + + name = osascript.osascript(name_code)[1] + artist = osascript.osascript(artist_code)[1] + duration = int(osascript.osascript(duration_code)[1]) / 1000 # Convert duration from ms to s + + return name, artist, duration + except Exception as e: + print(f"Failed to get current track info: {e}") + return None, None, None diff --git a/mattermost.py b/mattermost.py new file mode 100644 index 0000000..ac635fe --- /dev/null +++ b/mattermost.py @@ -0,0 +1,43 @@ +from datetime import datetime, timedelta, timezone +from dotenv import load_dotenv +import os +import requests + +load_dotenv() + + +class Mattermost: + def __init__(self, user_id="me"): + host = os.getenv("MATTERMOST_SERVER_URL", "http://localhost") + access_token = os.getenv("MATTERMOST_ACCESS_TOKEN", "") + self.user_id = user_id + self.url = f"{host}/api/v4/users/{self.user_id}/status/custom" + self.headers = { + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json", + } + + def set_status(self, status, emoji, expires_at=None): + data = { + "emoji": emoji, + "text": status, + "expires_at": expires_at, + } + response = requests.put(self.url, headers=self.headers, json=data) + if response.status_code != 200: + print(f"Failed to set Mattermost status: {response.content}") + raise Exception(f"Failed to set Mattermost status: {response.content}") + + def set_now_playing(self, track, artist, duration): + expires_at = (datetime.now(timezone.utc) + timedelta(seconds=duration)).astimezone() + status = f"{track} - {artist}" + print(f"Setting Mattermost status to {status} until {expires_at}") + self.set_status(status, "headphones", expires_at.isoformat()) + + + def get_status(self): + response = requests.get(self.url, headers=self.headers) + if response.status_code != 200: + print(f"Failed to get Mattermost status: {response.content}") + raise Exception(f"Failed to get Mattermost status: {response.content}") + return response.json() diff --git a/music.py b/music.py new file mode 100644 index 0000000..ac1c82d --- /dev/null +++ b/music.py @@ -0,0 +1,9 @@ +from mattermost import Mattermost + +class Music: + def __init__(self, connector): + self.mattermost = Mattermost() + self.connector = connector() + + def get_current_track_info(self) -> tuple: + return self.connector.get_current_track_info() diff --git a/music_app.py b/music_app.py new file mode 100644 index 0000000..6c3693d --- /dev/null +++ b/music_app.py @@ -0,0 +1,43 @@ +from datetime import datetime +import time +from music import Music +from mattermost import Mattermost +from connectors.apple_music import AppleMusic +from connectors.spotify import Spotify +from dotenv import load_dotenv +import os + +load_dotenv() + +SLEEP_TIME = 3 +MUSIC_APP = ( + os.getenv("MUSIC_APP", "apple_music").replace("_", " ").title().replace(" ", "") +) + + +def playing_now() -> tuple: + music = Music(connector=globals()[MUSIC_APP]) + return music.get_current_track_info() + + +def set_now_playing(name, artist, duration): + now = datetime.now().strftime("%H:%M:%S") + print(f"{now} 🎧 {name} - {artist} ⏲️ {duration}") + if name and artist and duration: + Mattermost().set_now_playing(name, artist, duration) + + +def main(): + name_curr, artist_curr, duration_curr = playing_now() + set_now_playing(name_curr, artist_curr, duration_curr) + + while True: + name, artist, duration = playing_now() + if name != name_curr: + set_now_playing(name, artist, duration) + name_curr = name + time.sleep(SLEEP_TIME) + + +if __name__ == "__main__": + main() diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..7f722d5 --- /dev/null +++ b/readme.txt @@ -0,0 +1,27 @@ +# Music Status Updater + +This is a Python application that fetches the currently playing track from either Apple Music or Spotify and sets it as your status on Mattermost. + +## Features +- Fetches current playing track from `Apple Music` or `Spotify` +- Sets the track as your status on Mattermost +- Using Appscript and Osascript for Music Track Information on MacOS + +## Dependencies +- MacOS +- Python 3 +- Apple Music or Spotify +- Mattermost + +## Environment Variables +`MUSIC_APP`: This variable determines which music service the application will fetch the currently playing track from. It can be either `apple_music` or `spotify`. + +## How to Run +1. Clone the repository +2. Install the dependencies with `pip install -r requirements.txt` +3. Set Mattermost token and host in `.env` +4. Set the `MUSIC_APP` environment variable to your preferred music service +5. Run the application with `python music_app.py` + +## Note +The application runs in an infinite loop, constantly checking for changes in the currently playing track and updating your Mattermost status accordingly. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..99a5a08 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +python-dotenv +requests +appscript +osascript