diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml deleted file mode 100644 index 3383c71..0000000 --- a/.github/workflows/dart.yml +++ /dev/null @@ -1,42 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Dart - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - # Note: This workflow uses the latest stable version of the Dart SDK. - # You can specify other versions if desired, see documentation here: - # https://github.com/dart-lang/setup-dart/blob/main/README.md - # - uses: dart-lang/setup-dart@v1 - - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 - - - name: Install dependencies - run: dart pub get - - # Uncomment this step to verify the use of 'dart format' on each commit. - # - name: Verify formatting - # run: dart format --output=none --set-exit-if-changed . - - # Consider passing '--fatal-infos' for slightly stricter analysis. - - name: Analyze project source - run: dart analyze - - # Your project will need to have tests in test/ and a dependency on - # package:test for this step to succeed. Note that Flutter projects will - # want to change this to 'flutter test'. - - name: Run tests - run: dart test diff --git a/README.md b/README.md index 3816eca..f20931f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -A sample command-line application with an entrypoint in `bin/`, library code -in `lib/`, and example unit test in `test/`. +> This branch aim at rewriting orbit in python for an easier code to read and use diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index dee8927..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options diff --git a/bin/orbit.dart b/bin/orbit.dart deleted file mode 100644 index 1006b28..0000000 --- a/bin/orbit.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:dotenv/dotenv.dart'; -import 'package:orbit/api.dart'; -import 'package:orbit/display/display_manager.dart'; -import 'package:orbit/display/led_display.dart'; -import 'package:orbit/display/view.dart'; - -void main() async { - var env = DotEnv(includePlatformEnvironment: true)..load(); - assert(env.isDefined("API_KEY")); - - final DisplayManager displayManager = DisplayManager( - LastFM( - params: Params(apiKey: env["API_KEY"]!, user: "apoleon33"), - ), - ); - - displayManager.displays.add( - Interface( - LastFM( - params: Params(apiKey: env["API_KEY"]!, user: "apoleon33"), - ), - ), - ); - - displayManager.displays.add(LedDisplay()); - - for (;;) { - await displayManager.display(); - } -} diff --git a/bin/orbit.py b/bin/orbit.py new file mode 100644 index 0000000..c7c0cf3 --- /dev/null +++ b/bin/orbit.py @@ -0,0 +1,27 @@ +import os + +from dotenv import load_dotenv + +from lib.api import LastFM, Params +from lib.display.display_manager import DisplayManager +from lib.display.view import Interface + +if __name__ == "__main__": + load_dotenv() + api_key = os.getenv("API_KEY") + assert api_key is not None, "No LastFM api key found" + + displayManager = DisplayManager( + LastFM( + Params(apiKey=api_key, user="apoleon33"), + ) + ) + + displayManager.displays.append(Interface( + LastFM( + Params(apiKey=api_key, user="apoleon33"), + ) + )) + + while True: + displayManager.display() diff --git a/lib/api.dart b/lib/api.dart deleted file mode 100644 index 816d638..0000000 --- a/lib/api.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:orbit/track.dart'; - -/// A class representing the parameters required for Last.fm API requests. -class Params { - /// The API key for authenticating requests with the Last.fm API. - final String apiKey; - - /// The Last.fm username for whom data is requested. - final String user; - - /// The format of the API response. Possible values are `"json"` (default) or `"xml"` (xml does break the [LastFM] class use). - final String format; - final String method; - - Params({ - required this.apiKey, - required this.user, - this.format = "json", - this.method = "user.getrecenttracks", - }); - - @override - String toString() => - "?api_key=$apiKey&method=$method&user=$user&format=$format"; -} - -/// A service class for interacting with the Last.fm API to retrieve user track data. -class LastFM { - final Params params; - final String baseUrl = "https://ws.audioscrobbler.com/2.0/"; - final Dio dio = Dio(); - - String? totalScrobbles; - LastFM({required this.params}); - - /// Makes a GET request to the Last.fm API using the provided parameters. - /// Returns the response data as a [Map]. - Future _callApi() async { - // (await dio.get("$baseUrl$params")).data; - Response call = await dio.get("$baseUrl$params"); - if (call.data["error"] != null) { - throw Exception( - "Error occured while fetching LastmFM's api: ${call.data["message"]}", - ); - } - - totalScrobbles = call.data["recenttracks"]["@attr"]["total"]; - return call.data; - } - - /// Checks if the user is currently playing a track on Last.fm. - Future isUserNowPlaying() async => - (await _callApi())["recenttracks"]["track"][0]["@attr"] != null; - - /// Retrieves the most recent track played by the user. - /// Returns a [Track] instance. - Future getLastTrack() async => - Track.createFromData((await _callApi())["recenttracks"]["track"][0]); - - String get username => params.user; - - Future get totalScrobble async => (totalScrobbles != null) - ? totalScrobbles - : (await _callApi())["recenttracks"]["@attr"]["total"]; -} diff --git a/lib/api.py b/lib/api.py new file mode 100644 index 0000000..8324706 --- /dev/null +++ b/lib/api.py @@ -0,0 +1,61 @@ +import requests + +from lib.track import Track + + +class Params: + apiKey: str + + user: str + + format: str + method: str + + _totalScrobbles: int | None = None + + def __init__(self, apiKey, user, format="json", method="user.getrecenttracks"): + self.apiKey = apiKey + self.user = user + self.format = format + self.method = method + + def __str__(self): + return f"?api_key={self.apiKey}&method={self.method}&user={self.user}&format={self.format}" + + +class LastFM: + params: Params + + baseUrl: str = "https://ws.audioscrobbler.com/2.0/" + + totalScrobbles: str | None + + def __init__(self, params: Params): + self.params = params + + def _callApi(self) -> dict: + call = requests.get(f"{self.baseUrl}{self.params}").json() + + if "error" in call: + raise RuntimeError(f"Error occured while fetching LastmFM's api: {call['message']}") + + self._totalScrobbles = call["recenttracks"]["@attr"]["total"] + + return call + + def isUserNowPlaying(self) -> bool: + """Check if the user is currently playing a track on Last.fm""" + apiCall = self._callApi() + return "@attr" in apiCall["recenttracks"]["track"][0] + + def getLastTrack(self) -> Track: + """Retrieves the most recent track played by the user.""" + + return Track.createFromData(self._callApi()["recenttracks"]["track"][0]) + + @property + def username(self): return self.params.user + + @property + def totalScrobbles(self): return self.totalScrobbles if self.totalScrobbles is not None else \ + self._callApi()["recenttracks"]["@attr"]["total"] diff --git a/lib/display/display.dart b/lib/display/display.dart deleted file mode 100644 index 3b05240..0000000 --- a/lib/display/display.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'dart:io'; - -import 'package:colorgram/colorgram.dart'; -import 'package:http/http.dart' as http; -import 'package:image/image.dart' as img; -import 'package:orbit/api.dart'; - -/// Abstract class for the multiples way of displaying the infos from the api (in a terminal, on a led strip...) -abstract class Display { - final LastFM api; - - /// The number of colors extracted from the album cover. - final int colorNUmber = 6; - - Display(this.api); - - Future?> getColorPalette(String imageUrl) async { - try { - final response = await http.get(Uri.parse(imageUrl)); - - if (response.statusCode == 200) { - // Décoder l'image en mémoire - img.Image? image = img.decodeImage(response.bodyBytes); - - if (image != null) { - // Solution temporaire: écrire dans un fichier mémoire - final tempFile = File('temp_image.jpg'); - await tempFile.writeAsBytes(img.encodeJpg(image)); - - List colors = extractColor(tempFile, colorNUmber); - - await tempFile.delete(); - - return colors; - } - } - } catch (e) { - print('Erreur: $e'); - return null; - } - return null; - } - - Future display(); -} diff --git a/lib/display/display.py b/lib/display/display.py new file mode 100644 index 0000000..55b8217 --- /dev/null +++ b/lib/display/display.py @@ -0,0 +1,32 @@ +from abc import ABC, abstractmethod + +import requests +from Pylette import extract_colors, Palette + +from lib.api import LastFM + + +class Display(ABC): + api: LastFM + + # The number of colors extracted from the album cover. + colorNumber:int = 6 + + def __init__(self, api: LastFM): + self.api = api + + def getColorPalette(self, imageUrl:str) -> Palette: + img = requests.get(imageUrl).content + tempFile = open("temp.jpg", "wb") + + tempFile.write(img) + tempFile.close() + + palette = extract_colors(image='temp.jpg', palette_size=self.colorNumber) + + return palette + + @abstractmethod + def display(self): pass + + diff --git a/lib/display/display_manager.dart b/lib/display/display_manager.dart deleted file mode 100644 index 7a7aa0a..0000000 --- a/lib/display/display_manager.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:io'; - -import 'package:orbit/display/display.dart'; -import 'package:orbit/display/requestless_display.dart'; -import 'package:orbit/track.dart'; - -class DisplayManager extends Display { - List displays = List.empty(growable: true); - - final int delay; - - DisplayManager(super.api, {this.delay = 15}); - - @override - Future display() async { - final Track lastTrack = await api.getLastTrack(); - - bool nowPlayingStatus = await api.isUserNowPlaying(); - - if (nowPlayingStatus) { - for (var display in displays) { - await display.show( - ColoredTrack.createFromTrack( - lastTrack, - (await getColorPalette(lastTrack.image[0].url))!, - ), - ); - } - } else { - for (var display in displays) { - await display.showNoTrack(); - } - } - - sleep(Duration(seconds: delay)); - } -} diff --git a/lib/display/display_manager.py b/lib/display/display_manager.py new file mode 100644 index 0000000..dca127d --- /dev/null +++ b/lib/display/display_manager.py @@ -0,0 +1,30 @@ +import time + +from lib.display.display import Display +from lib.display.requestless_display import RequestlessDisplay +from lib.track import ColoredTrack + + +class DisplayManager(Display): + displays: list[RequestlessDisplay] = [] + + delay: int + + def __init__(self, api_key, delay=15): + super().__init__(api_key) + self.delay = delay + + def display(self): + lastTrack = self.api.getLastTrack() + nowPlayingStatus = self.api.isUserNowPlaying() + + if nowPlayingStatus: + for display in self.displays: + display.show( + ColoredTrack.createFromTrack( + lastTrack, + self.getColorPalette(lastTrack.images[0].url), + ) + ) + + time.sleep(self.delay) diff --git a/lib/display/requestless_display.dart b/lib/display/requestless_display.dart deleted file mode 100644 index 8ca4e9d..0000000 --- a/lib/display/requestless_display.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:orbit/track.dart'; - -abstract class RequestlessDisplay { - Future show(ColoredTrack track); - - Future showNoTrack(); -} diff --git a/lib/display/requestless_display.py b/lib/display/requestless_display.py new file mode 100644 index 0000000..5ecb8d2 --- /dev/null +++ b/lib/display/requestless_display.py @@ -0,0 +1,7 @@ +from abc import ABC + +from lib.track import ColoredTrack + + +class RequestlessDisplay(ABC): + def show(self, track: ColoredTrack): pass diff --git a/lib/display/view.dart b/lib/display/view.dart deleted file mode 100644 index d30c9de..0000000 --- a/lib/display/view.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'dart:io'; - -import 'package:http/http.dart' as http; -import 'package:image/image.dart' as img; -import 'package:orbit/display/display.dart'; -import 'package:orbit/display/requestless_display.dart'; -import 'package:orbit/track.dart'; -import 'package:tint/tint.dart'; - -import 'package:colorgram/colorgram.dart'; - -class Interface extends Display implements RequestlessDisplay { - /// How many lines the display of the current song takes in the terminal. - final int nowPLayingLineNumber = 8; - - /// How many lines the "no music detected" text takes in the terminal. - final int notPlayingLineNumber = 1; - - final String leftPadding = " "; - - final int coverDimensions = 20; // in pixel - - final String delimiter = "------------------------"; - - Interface(super.api); - - /// Returns a string colored like the ones given in arguments - String createColorPalette(List colors, {String char = "███"}) => - colors - .map( - (color) => color.percentage - .toStringAsFixed(2) - .replaceAll( - RegExp("0.00"), - char.rgb(r: color.r, g: color.g, b: color.b), - ) // weird workaround to avoid percentages on cover - .onRgb(r: color.r, g: color.g, b: color.b), - ) - .join(); - - // generated by Gemini - void _clearTerminal() { - if (Platform.isWindows) { - // This is a common way to clear the screen on Windows - // by executing the `cls` command. - try { - Process.runSync('cmd', ['/c', 'cls'], runInShell: true); - } catch (e) { - // Fallback for older Windows terminals or systems where `cmd` is not easily accessible. - print('\x1B[2J\x1B[0;0H'); - } - } else { - // For Linux, macOS, and modern terminals. - // The ANSI escape sequence for clearing the screen. - print('\x1B[2J\x1B[0;0H'); - } - } - - Future?> convertImgToAscii(Track track) async { - try { - final response = await http.get( - Uri.parse( - track.image - .where((img) => img.imageSize == ImageSize.extralarge) - .toList()[0] - .url, - ), - ); - - if (response.statusCode == 200) { - // Décoder l'image en mémoire - img.Image? image = img.decodeImage(response.bodyBytes); - if (image != null) { - img.Image resizedImage = img.copyResize( - image, - width: coverDimensions, - height: coverDimensions, - ); - - List> colors = List.empty(growable: true); - - for (var y = 0; y < coverDimensions; y++) { - colors.add([]); - - for (var x = 0; x < coverDimensions; x++) { - img.Pixel currentPixel = resizedImage.getPixel(x, y); - - colors[y].add( - CgColor( - currentPixel.r.toInt(), - currentPixel.g.toInt(), - currentPixel.b.toInt(), - 0.0, - //(1 / (coverDimensions * coverDimensions)) * 100, - ), - ); - } - } - - return colors - .map((elem) => createColorPalette(elem, char: "▓▓")) - .toList(); - } - } - } catch (e) { - print('Erreur: $e'); - return null; - } - return null; - } - - Future formatOutput(Track track) async { - List dominantColors = (await getColorPalette( - track.image - .where((img) => img.imageSize == ImageSize.extralarge) - .toList()[0] - .url, - ))!; - - List albumCover = (await convertImgToAscii(track))!; - - // let's avoid making one long line - String output = "$leftPadding${albumCover[0]}"; - output += "$leftPadding${"Music detected!".bold().red()}\n"; - output += "$leftPadding${albumCover[1]}"; - output += "$leftPadding${"Name".bold().red()}: ${track.name.italic()}\n"; - output += "$leftPadding${albumCover[2]}"; - output += - "$leftPadding${"Album".bold().red()}: ${track.album.name.italic()}\n"; - output += "$leftPadding${albumCover[3]}"; - output += - "$leftPadding${"Artist".bold().red()}: ${track.artist.name.italic()}\n"; - output += "$leftPadding${albumCover[4]}"; - output += - "$leftPadding${"Image".bold().red()}: ${track.image.last.url.substring("https://lastfm.freetls.fastly.net/i/u/300x300/".length)}\n"; - output += "$leftPadding${albumCover[5]}"; - output += "$leftPadding${createColorPalette(dominantColors)}\n"; - output += "$leftPadding${albumCover[6]}"; - output += "$leftPadding$delimiter\n"; - output += "$leftPadding${albumCover[7]}"; - output += - "$leftPadding${"LastFM Username".bold().red()}: ${api.username}\n"; - output += "$leftPadding${albumCover[8]}"; - output += - "$leftPadding${"Total account scrobbles".bold().red()}: ${await api.totalScrobble}\n"; - - for (var row in albumCover.sublist(nowPLayingLineNumber)) { - output += "$leftPadding$row\n"; - } - - return output; - } - - @override - Future display() async { - final Track lastTrack = await api.getLastTrack(); - - bool nowPlayingStatus = await api.isUserNowPlaying(); - - if (nowPlayingStatus) { - // if (lastTrack != _currentTrack) { - // reload the whole display only when the track changes - - _clearTerminal(); - print((await formatOutput(lastTrack))); - } else { - _clearTerminal(); - print("no music currently playing, retrying in 15s...".bold().blue()); - } - } - - @override - Future show(ColoredTrack track) async { - _clearTerminal(); - print( - (await formatOutput( - Track( - track.name, - track.mbid, - artist: track.artist, - album: track.album, - image: track.image, - url: track.url, - ), - )), - ); - } - - @override - Future showNoTrack() async { - _clearTerminal(); - print("no music currently playing, retrying in 15s...".bold().blue()); - } -} diff --git a/lib/display/view.py b/lib/display/view.py new file mode 100644 index 0000000..8cfaa0b --- /dev/null +++ b/lib/display/view.py @@ -0,0 +1,48 @@ +# python +import os + +from lib.display.display import Display +from lib.display.requestless_display import RequestlessDisplay + + +class Interface(Display, RequestlessDisplay): + def __init__(self, api): + super().__init__(api) + self.now_playing_line_number = 8 + self.not_playing_line_number = 1 + self.left_padding = " " + self.cover_dimensions = 20 + self.delimiter = "------------------------" + + def _clearTerminal(self): + if os.name == "nt": + os.system("cls") + else: + os.system("clear") + + def format_output(self, track) -> str: + output = "Music detected!\n" + + output += f"Name: {track.name}\n" + output += f"Artist: {track.artist.name}\n" + output += f"Album: {track.album.name}\n" + output += f"{self.delimiter}\n" + output += f"LastFM Username: {self.api.username}\n" + + return output + + def display(self): + last_track = self.api.getLastTrack() + now_playing_status = self.api.isUserNowPlaying() + if now_playing_status: + self._clearTerminal() + print(self.format_output(last_track)) + print(self.delimiter) + print() + else: + self._clearTerminal() + print("no music currently playing, retrying in 15s...") + + def show(self, track): + self._clearTerminal() + print(self.format_output(track)) diff --git a/lib/track.dart b/lib/track.dart deleted file mode 100644 index 591816c..0000000 --- a/lib/track.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:colorgram/colorgram.dart'; - -/// An abstract class that represent what LastFM "entities" (track, albums, artists...) have in common. -abstract class LastFMEntity { - /// The **MusicBrainz ID (MBID)** for this entity. - /// - /// This unique identifier comes from the [MusicBrainz database](https://musicbrainz.org/), - /// an open-source music encyclopedia. - final String mbid; - final String name; - - LastFMEntity(this.name, this.mbid); - - @override - bool operator ==(Object other) { - assert( - other.runtimeType == Track, - "Only Track to Track comparison implemented atm", - ); - return (other as Track).mbid == mbid; - } - - @override - int get hashCode => mbid.hashCode; - - @override - String toString() => "name: $name, mbid: $mbid"; -} - -class Artist extends LastFMEntity { - Artist(super.name, super.mbid); - - Artist.createFromData(Map data) - : this(data["artist"]["#text"], data["artist"]["mbid"]); -} - -class Album extends LastFMEntity { - Album(super.name, super.mbid); - - Album.createFromData(Map data) - : this(data["album"]["#text"], data["album"]["mbid"]); -} - -enum ImageSize { small, medium, large, extralarge } - -class Image { - final ImageSize imageSize; - final String url; - - Image(this.url, this.imageSize); - - @override - String toString() => "url: $url, size: $imageSize"; -} - -class Track extends LastFMEntity { - final Artist artist; - final Album album; - final List image; - final String url; - - Track( - super.name, - super.mbid, { - required this.artist, - required this.album, - required this.image, - required this.url, - }); - - Track.createFromData(Map data) - : this( - data["name"], - data["mbid"], - artist: Artist.createFromData(data), - album: Album.createFromData(data), - image: (data["image"] as List) - .map( - (elem) => Image( - elem["#text"], - ImageSize.values.firstWhere( - (size) => size.name == elem["size"], - ), - ), - ) - .toList(), - url: data["url"], - ); - - @override - String toString() => - "Track name: $name \nArtist $artist \nurl: $url \nimages: $image"; -} - -/// the [Track] class, but with also the color palette of the album. -class ColoredTrack extends Track { - final List palette; - ColoredTrack( - super.name, - super.mbid, - this.palette, { - required super.artist, - required super.album, - required super.image, - required super.url, - }); - - ColoredTrack.createFromTrack(Track track, List palette) - : this( - track.name, - track.mbid, - palette, - artist: track.artist, - album: track.album, - image: track.image, - url: track.url, - ); -} diff --git a/lib/track.py b/lib/track.py new file mode 100644 index 0000000..aeba267 --- /dev/null +++ b/lib/track.py @@ -0,0 +1,105 @@ +from abc import ABC +from enum import Enum + +from Pylette import Palette + + +class LastFMEntity(ABC): + """ + An abstract class that represent what LastFM "entities" (track, albums, artists...) have in common. + """ + + mbid: str + "The **MusicBrainz ID (MBID)** for this entity. This unique identifier comes from the [MusicBrainz database](https://musicbrainz.org/), an open-source music encyclopedia." + + name: str + + def __init__(self, mbid, name): + self.mbid = mbid + self.name = name + + def __eq__(self, __value): + assert type(__value) is Track, "Only Track to Track comparison implemented atm" + return __value.mbid == self.mbid + + def __str__(self): + return f"name: {self.name}, mbid: {self.mbid}" + + +class Artist(LastFMEntity): + def __init__(self, mbid, name): + super().__init__(mbid, name) + + @staticmethod + def createFromData(data: dict): + return Artist(data["artist"]["#text"], data["artist"]["mbid"]) + + +class Album(LastFMEntity): + def __init__(self, mbid, name): + super().__init__(mbid, name) + + @staticmethod + def createFromData(data: dict): + return Album(data["album"]["mbid"], data["album"]["#text"]) + + +class ImageSize(Enum): + small = 1 + medium = 2 + large = 3 + extraLarge = 4 + + +class Image: + imageSize: ImageSize + url: str + + def __init__(self, imageSize: ImageSize, url): + self.imageSize = imageSize + self.url = url + + def __str__(self): + return f"url: {self.url}, size: {self.imageSize}" + + +class Track(LastFMEntity): + artist: Artist + album: Album + images: list[Image] + url: str + + def __init__(self, name, mbid, artist: Artist, album: Album, images: list[Image], url: str): + super().__init__(mbid, name) + self.artist = artist + self.album = album + self.images = images + self.url = url + + @staticmethod + def createFromData(data: dict): + imageList = [] + for image in data["image"]: + imageList.append( + Image( + ImageSize.extraLarge, + image["#text"] + ) + ) + + return Track(data["name"], data["mbid"], Artist.createFromData(data), Album.createFromData(data), imageList, + data["url"]) + + +class ColoredTrack(Track): + palette: Palette + + def __init__(self, name, mbid, palette, artist, album, images, url): + super().__init__(name, mbid, artist, album, images, url) + + self.palette = palette + + @staticmethod + def createFromTrack(track: Track, palette: Palette): + return ColoredTrack(name=track.name, mbid=track.mbid, palette=palette, artist=track.artist, album=track.album, + images=track.images, url=track.url) diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 815c55a..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,517 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: f7bac1065b51df46b2291296e1c1b3616a47aeb735aea46a8ca3dcb7bb700ee7 - url: "https://pub.dev" - source: hosted - version: "86.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "4001e2de7c9d125af9504b4c4f64ebba507c9cb9c712caf02ac1d4c37824f58c" - url: "https://pub.dev" - source: hosted - version: "8.0.0" - archive: - dependency: transitive - description: - name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" - url: "https://pub.dev" - source: hosted - version: "4.0.7" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - cli_config: - dependency: transitive - description: - name: cli_config - sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.dev" - source: hosted - version: "0.2.0" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - colorgram: - dependency: "direct main" - description: - name: colorgram - sha256: b02db8fc5906b4cec18c1dbb8c34143a1cb20d841817af58c37c8c7275d441ad - url: "https://pub.dev" - source: hosted - version: "1.0.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - coverage: - dependency: transitive - description: - name: coverage - sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" - url: "https://pub.dev" - source: hosted - version: "1.15.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - dartpy: - dependency: "direct main" - description: - name: dartpy - sha256: "006e20dc13596977d953f16aa2256bd2b833cfb8553f6e7eebfd8243ef548daa" - url: "https://pub.dev" - source: hosted - version: "0.0.5" - dio: - dependency: "direct main" - description: - name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" - url: "https://pub.dev" - source: hosted - version: "5.8.0+1" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - dotenv: - dependency: "direct main" - description: - name: dotenv - sha256: "379e64b6fc82d3df29461d349a1796ecd2c436c480d4653f3af6872eccbc90e1" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - ffi: - dependency: "direct main" - description: - name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 - url: "https://pub.dev" - source: hosted - version: "2.4.4" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - http: - dependency: "direct main" - description: - name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" - source: hosted - version: "4.1.2" - image: - dependency: "direct main" - description: - name: image - sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" - url: "https://pub.dev" - source: hosted - version: "4.5.4" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" - source: hosted - version: "0.7.2" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - lints: - dependency: "direct dev" - description: - name: lints - sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 - url: "https://pub.dev" - source: hosted - version: "6.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" - source: hosted - version: "0.12.17" - meta: - dependency: transitive - description: - name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" - source: hosted - version: "1.17.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: "9436fe11f82d7cc1642a8671e5aa4149ffa9ae9116e6cf6dd665fc0653e3825c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 - url: "https://pub.dev" - source: hosted - version: "1.1.3" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b - url: "https://pub.dev" - source: hosted - version: "2.1.2" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" - url: "https://pub.dev" - source: hosted - version: "0.10.13" - source_span: - dependency: transitive - description: - name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" - source: hosted - version: "1.10.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test: - dependency: "direct dev" - description: - name: test - sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" - url: "https://pub.dev" - source: hosted - version: "1.26.3" - test_api: - dependency: transitive - description: - name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 - url: "https://pub.dev" - source: hosted - version: "0.7.7" - test_core: - dependency: transitive - description: - name: test_core - sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" - url: "https://pub.dev" - source: hosted - version: "0.6.12" - tint: - dependency: "direct main" - description: - name: tint - sha256: "9652d9a589f4536d5e392cf790263d120474f15da3cf1bee7f1fdb31b4de5f46" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" - source: hosted - version: "15.0.2" - watcher: - dependency: transitive - description: - name: watcher - sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - xml: - dependency: transitive - description: - name: xml - sha256: "3202a47961c1a0af6097c9f8c1b492d705248ba309e6f7a72410422c05046851" - url: "https://pub.dev" - source: hosted - version: "6.6.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" -sdks: - dart: ">=3.8.2 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index c8d90ca..0000000 --- a/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: orbit -description: A sample command-line application. -version: 1.0.0 -repository: https://github.com/apoleon33/orbit - - -environment: - sdk: ^3.8.2 - -# Add regular dependencies here. -dependencies: - dio: ^5.8.0+1 - dotenv: ^4.2.0 - tint: ^2.0.1 - colorgram: 1.0.1 - http: ^1.4.0 - image: ^4.5.4 - dartpy: ^0.0.5 - ffi: ^2.1.4 - # path: ^1.8.0 - -dev_dependencies: - lints: ^6.0.0 - test: ^1.24.0