Show HN:Petrichor - Macos的免费,开源,离线音乐播放器
Show HN: Petrichor – a free, open-source, offline music player for macOS

原始链接: https://github.com/kushalpandya/Petrichor

Petrichor是由Swift and Swiftui建造的本地MacOS离线音乐播放器,旨在满足现代功能丰富的体验的需求。它通过扫描文件夹和利用Track Metadata来组织您的音乐库,并将信息存储在SQLITE数据库中(使用GRDB)。关键功能包括智能播放列表,Airplay 2支持,迷你播放器,自动更新和在线元数据获取。 该应用程序拥有综合的库管理,允许用户通过文件夹或有组织的视图浏览,创建播放列表,管理播放队列以及固定项目以快速访问。导航是无缝的,右键单击上下文菜单,用于访问相关内容。它提供本机MacOS集成,包括菜单栏控件,码头播放和暗模式支持。 Petrichor使用SQLite FTS5在大型音乐集中进行快速搜索,并使用Avfoundation进行播放。提供的数据库架构强调了文件夹,艺术家,专辑,曲目,播放列表和流派之间的关系,展示了该应用程序的数据组织。构建该应用需要MacOS 14+和Xcode。

Kushalpandya创建了Petrichor,这是Macos的免费,开源,离线音乐播放器,这是因为自从流媒体成为主流以来缺乏良好的离线播放器。 PETRICHOR允许用户管理具有有组织的浏览,播放列表创建,文件夹视图,固定收藏夹和轻松导航的功能的功能。它包括与Menubar控件,黑暗模式和快速搜索的本机MacOS集成。 用户欣赏干净的UI,专注于本地库,并建议使用智能播放列表,音量级别,iOS同步以及对有声读物的支持。 一些推荐其他玩家,例如Swinsian,Vox,Jellyfin等,同时提供有关其功能和限制的反馈。 在进口期间,还要求对元数据保存进行自制/MacPorts分发和支持。由于Apple的恶意软件检查,一些用户正面临打开应用程序的问题。它似乎具有FLAC支持。
相关文章

原文

Petrichor App Icon

An offline music player for macOS

Download for macOS

  • Everything you'd expect from an offline music player!
  • Map your music folders and browse your library in an organized view.
  • Create playlists and manage the play queue interactively.
  • Browse music using folder view when needed.
  • Pin anything (almost!) to the sidebar for quick access to your favorite music.
  • Navigate easily: right-click a track to go to its album, artist, year, etc.
  • Native macOS integration with menubar and dock playback controls, plus dark mode support.
  • Search quickly through large libraries containing thousands of songs.

💡 Tip: Petrichor relies heavily on tracks having good metadata for all its features to work well.

  • Smart playlists with user-configurable conditional filters
  • AirPlay 2 casting support
  • Miniplayer and full-screen modes
  • Automatic in-app updates
  • Online album & artist information fetching
  • ... and much more!
  • Go to Releases and download the latest .dmg.
  • Open the .dmg and drag the app icon into the Applications folder.
  • In Applications, right-click Petrichor > Open.

P.S. I plan publish it on Homebrew soon.

I have a large collection of music files that I’ve gathered over the years, and I missed having a good offline music player on macOS. I used Swinsian (great app, by the way!), but it hasn't been updated in years. I also missed features commonly found in streaming apps; so I built Petrichor to scratch that itch and learn Swift and macOS app development along the way!

  • Built entirely with Swift and SwiftUI for the best macOS integration.
  • Once folders are added, the app scans them and populates a SQLite database using GRDB.
  • Petrichor does not alter your music files, it only reads from the directories you add.
  • Tracks searching is powered by SQLite FTS5 with fall-back to in-memory search.
  • Playback is powered by AVFoundation.
View Database Schema
erDiagram
    folders {
        INTEGER id PK "AUTO_INCREMENT"
        TEXT name "NOT NULL"
        TEXT path "NOT NULL UNIQUE"
        INTEGER track_count "NOT NULL DEFAULT 0"
        DATETIME date_added "NOT NULL"
        DATETIME date_updated "NOT NULL"
        BLOB bookmark_data "Security-scoped bookmark"
    }

    artists {
        INTEGER id PK "AUTO_INCREMENT"
        TEXT name "NOT NULL"
        TEXT normalized_name "NOT NULL UNIQUE"
        TEXT sort_name
        BLOB artwork_data
        TEXT bio
        TEXT bio_source
        DATETIME bio_updated_at
        TEXT image_url
        TEXT image_source
        DATETIME image_updated_at
        TEXT discogs_id
        TEXT musicbrainz_id
        TEXT spotify_id
        TEXT apple_music_id
        TEXT country
        INTEGER formed_year
        INTEGER disbanded_year
        TEXT genres "JSON array"
        TEXT websites "JSON array"
        TEXT members "JSON array"
        INTEGER total_tracks "NOT NULL DEFAULT 0 CHECK >= 0"
        INTEGER total_albums "NOT NULL DEFAULT 0 CHECK >= 0"
        DATETIME created_at "NOT NULL"
        DATETIME updated_at "NOT NULL"
    }

    albums {
        INTEGER id PK "AUTO_INCREMENT"
        TEXT title "NOT NULL"
        TEXT normalized_title "NOT NULL"
        TEXT sort_title
        BLOB artwork_data
        TEXT release_date
        INTEGER release_year "CHECK 1900-2100"
        TEXT album_type
        INTEGER total_tracks "CHECK >= 0"
        INTEGER total_discs "CHECK >= 0"
        TEXT description
        TEXT review
        TEXT review_source
        TEXT cover_art_url
        TEXT thumbnail_url
        TEXT discogs_id
        TEXT musicbrainz_id
        TEXT spotify_id
        TEXT apple_music_id
        TEXT label
        TEXT catalog_number
        TEXT barcode
        TEXT genres "JSON array"
        DATETIME created_at "NOT NULL"
        DATETIME updated_at "NOT NULL"
    }

    album_artists {
        INTEGER album_id FK "NOT NULL"
        INTEGER artist_id FK "NOT NULL"
        TEXT role "NOT NULL DEFAULT 'primary'"
        INTEGER position "NOT NULL DEFAULT 0"
    }

    genres {
        INTEGER id PK "AUTO_INCREMENT"
        TEXT name "NOT NULL UNIQUE"
    }

    tracks {
        INTEGER id PK "AUTO_INCREMENT"
        INTEGER folder_id FK "NOT NULL"
        INTEGER album_id FK
        TEXT path "NOT NULL UNIQUE"
        TEXT filename "NOT NULL"
        TEXT title
        TEXT artist
        TEXT album
        TEXT composer
        TEXT genre
        TEXT year
        REAL duration "CHECK >= 0"
        TEXT format
        INTEGER file_size
        DATETIME date_added "NOT NULL"
        DATETIME date_modified
        BLOB track_artwork_data
        BOOLEAN is_favorite "NOT NULL DEFAULT false"
        INTEGER play_count "NOT NULL DEFAULT 0"
        DATETIME last_played_date
        BOOLEAN is_duplicate "NOT NULL DEFAULT false"
        INTEGER primary_track_id FK
        TEXT duplicate_group_id
        TEXT album_artist
        INTEGER track_number "CHECK > 0"
        INTEGER total_tracks
        INTEGER disc_number "CHECK > 0"
        INTEGER total_discs
        INTEGER rating "CHECK 0-5"
        BOOLEAN compilation "DEFAULT false"
        TEXT release_date
        TEXT original_release_date
        INTEGER bpm
        TEXT media_type "Music/Audiobook/Podcast"
        INTEGER bitrate "CHECK > 0"
        INTEGER sample_rate
        INTEGER channels "1=mono, 2=stereo"
        TEXT codec
        INTEGER bit_depth
        TEXT sort_title
        TEXT sort_artist
        TEXT sort_album
        TEXT sort_album_artist
        TEXT extended_metadata "JSON"
    }

    playlists {
        TEXT id PK "UUID"
        TEXT name "NOT NULL"
        TEXT type "NOT NULL (regular/smart)"
        BOOLEAN is_user_editable "NOT NULL"
        BOOLEAN is_content_editable "NOT NULL"
        DATETIME date_created "NOT NULL"
        DATETIME date_modified "NOT NULL"
        BLOB cover_artwork_data
        TEXT smart_criteria "JSON"
        INTEGER sort_order "NOT NULL DEFAULT 0"
    }

    playlist_tracks {
        TEXT playlist_id FK "NOT NULL"
        INTEGER track_id FK "NOT NULL"
        INTEGER position "NOT NULL"
        DATETIME date_added "NOT NULL"
    }

    track_artists {
        INTEGER track_id FK "NOT NULL"
        INTEGER artist_id FK "NOT NULL"
        TEXT role "NOT NULL DEFAULT 'artist'"
        INTEGER position "NOT NULL DEFAULT 0"
    }

    track_genres {
        INTEGER track_id FK "NOT NULL"
        INTEGER genre_id FK "NOT NULL"
    }

    pinned_items {
        INTEGER id PK "AUTO_INCREMENT"
        TEXT item_type "NOT NULL (library/playlist)"
        TEXT filter_type "For library items"
        TEXT filter_value "Artist/album name"
        TEXT entity_id "UUID for entities"
        INTEGER artist_id "Database ID"
        INTEGER album_id "Database ID"
        TEXT playlist_id "For playlist items"
        TEXT display_name "NOT NULL"
        TEXT subtitle "For albums"
        TEXT icon_name "NOT NULL"
        INTEGER sort_order "NOT NULL DEFAULT 0"
        DATETIME date_added "NOT NULL"
    }

    tracks_fts {
        INTEGER track_id "NOT INDEXED"
        TEXT title
        TEXT artist
        TEXT album
        TEXT album_artist
        TEXT composer
        TEXT genre
        TEXT year
    }

    folders ||--o{ tracks : contains
    albums ||--o{ album_artists : "has artists"
    artists ||--o{ album_artists : "appears on"
    albums ||--o{ tracks : contains
    artists ||--o{ track_artists : "appears in"
    tracks ||--o{ track_artists : "has artists"
    tracks ||--o| tracks : "duplicate of"
    genres ||--o{ track_genres : "categorizes"
    tracks ||--o{ track_genres : "has genres"
    playlists ||--o{ playlist_tracks : contains
    tracks ||--o{ playlist_tracks : "appears in"
    tracks ||--|| tracks_fts : "searchable in"
Loading
  • Make sure you’re running macOS 14 or later.
  • Install Xcode.
  • To build the .dmg installer using the build-installer.sh script, install:

MIT

联系我们 contact @ memedata.com