Skip to content

Commit

Permalink
Menu item to remove downloaded lyrics, fixed lyrics precedence
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-venugopal committed Jan 14, 2025
1 parent 205a7d5 commit 97d2d86
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 27 deletions.
4 changes: 4 additions & 0 deletions Aural.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@
3E9E590E25DB6B150064EB5F /* MenuBarAppModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E58FF25DB6B150064EB5F /* MenuBarAppModeController.swift */; };
3E9E590F25DB6B150064EB5F /* AppModeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E590025DB6B150064EB5F /* AppModeManager.swift */; };
3E9E591025DB6B150064EB5F /* ModularAppModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E590125DB6B150064EB5F /* ModularAppModeController.swift */; };
3E9F0AE72D370A2600A23BDE /* TrackInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9F0AE62D370A2600A23BDE /* TrackInitializer.swift */; };
3EA0102925E2725900A80DAC /* FontSchemeHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA0102825E2725900A80DAC /* FontSchemeHistory.swift */; };
3EA0102D25E28C4C00A80DAC /* FontSchemeChangeContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA0102C25E28C4C00A80DAC /* FontSchemeChangeContext.swift */; };
3EA07ACA2D27D22B001A5124 /* LyricsPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA07AC92D27D22B001A5124 /* LyricsPreferences.swift */; };
Expand Down Expand Up @@ -1972,6 +1973,7 @@
3E9E58FF25DB6B150064EB5F /* MenuBarAppModeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuBarAppModeController.swift; sourceTree = "<group>"; };
3E9E590025DB6B150064EB5F /* AppModeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModeManager.swift; sourceTree = "<group>"; };
3E9E590125DB6B150064EB5F /* ModularAppModeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModularAppModeController.swift; sourceTree = "<group>"; };
3E9F0AE62D370A2600A23BDE /* TrackInitializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackInitializer.swift; sourceTree = "<group>"; };
3E9F692126B9CE26001FAFA3 /* PlaylistControlsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistControlsContainer.swift; sourceTree = "<group>"; };
3EA0102825E2725900A80DAC /* FontSchemeHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSchemeHistory.swift; sourceTree = "<group>"; };
3EA0102C25E28C4C00A80DAC /* FontSchemeChangeContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSchemeChangeContext.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3087,6 +3089,7 @@
3E0217782C23490E00865AC2 /* FileReaderProtocol.swift */,
3E0217792C23490E00865AC2 /* TrackReader.swift */,
3E28A5782D33DCCB007331AF /* TrackReader+Lyrics.swift */,
3E9F0AE62D370A2600A23BDE /* TrackInitializer.swift */,
);
path = TrackIO;
sourceTree = "<group>";
Expand Down Expand Up @@ -6170,6 +6173,7 @@
3E0218D52C23490E00865AC2 /* PlaybackContextProtocol.swift in Sources */,
3E9DA1972D342B080094D6DE /* TextAndImageButtonCell.swift in Sources */,
3E7E9F4E2C66B1AF0011DE8E /* WaveformWindowController.swift in Sources */,
3E9F0AE72D370A2600A23BDE /* TrackInitializer.swift in Sources */,
3E02189A2C23490E00865AC2 /* MenuBarPlayerUIPersistentState.swift in Sources */,
3E02195C2C23490E00865AC2 /* SearchResults.swift in Sources */,
3E6EFFA42815309500C813AC /* TextColorSchemeViewController.swift in Sources */,
Expand Down
1 change: 0 additions & 1 deletion Source/Core/Constants/FilesAndPaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ struct FilesAndPaths {

// Default user's music directory (default place to look in, when opening/saving files)
static let musicDir: URL = homeDir.appendingPathComponent("/Music", isDirectory: true).resolvedURL
// static let musicDir: URL = URL(fileURLWithPath: "/Volumes/MBP-Ext-4TB/Projects/Aural-Test/Aural-Music")

static let baseDir: URL = musicDir.appendingPathComponent("aural", isDirectory: true)
static let metadataDir: URL = baseDir.appendingPathComponent("metadata", isDirectory: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ struct TrackInfoUpdatedNotification: NotificationPayload {
// The track info fields that have been updated. Different UI components may display different fields.
let updatedFields: Set<UpdatedTrackInfoField>

init(updatedTrack: Track, updatedFields: UpdatedTrackInfoField...) {
let destructiveUpdate: Bool

init(updatedTrack: Track, updatedFields: UpdatedTrackInfoField..., destructiveUpdate: Bool = false) {

self.updatedTrack = updatedTrack
self.updatedFields = Set(updatedFields)
self.destructiveUpdate = destructiveUpdate
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extension Notification.Name {
static let loadFromFile = Notification.Name("lyrics_loadFromFile")
static let addLyricsFile = Notification.Name("lyrics_addLyricsFile")
static let searchForLyricsOnline = Notification.Name("lyrics_searchForLyricsOnline")
static let removeDownloadedLyrics = Notification.Name("lyrics_removeDownloadedLyrics")
static let karaokeModePreferenceUpdated = Notification.Name("lyrics_karaokeModePreferenceUpdated")
}
}
2 changes: 2 additions & 0 deletions Source/Core/Persistence/Model/MetadataPersistentState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct FileMetadataPersistentState: Codable {
let timedLyrics: TimedLyricsPersistentState?

let externalLyricsFile: URL?
let lyricsDownloaded: Bool?

let nonEssentialMetadata: [String: MetadataEntry]

Expand Down Expand Up @@ -100,6 +101,7 @@ struct FileMetadataPersistentState: Codable {
}

self.externalLyricsFile = metadata.externalLyricsFile
self.lyricsDownloaded = metadata.lyricsDownloaded

self.nonEssentialMetadata = metadata.nonEssentialMetadata

Expand Down
4 changes: 2 additions & 2 deletions Source/Core/PlayQueue/Delegate/PlayQueueDelegate+Init.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ extension PlayQueueDelegate {

case .rememberFromLastAppLaunch:

if let tracks = persistentState?.tracks, tracks.isNonEmpty {
loadTracks(from: tracks, params: pqParmsWithAutoplayAndNoHistory)
if let urls = persistentState?.tracks, urls.isNonEmpty {
loadTracks(from: urls, params: pqParmsWithAutoplayAndNoHistory)
}

case .loadPlaylistFile:
Expand Down
5 changes: 5 additions & 0 deletions Source/Core/TrackIO/Model/FileMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class FileMetadata {

var externalLyricsFile: URL?
var externalTimedLyrics: TimedLyrics?
var lyricsDownloaded: Bool = false

var nonEssentialMetadata: [String: MetadataEntry] = [:]

Expand Down Expand Up @@ -140,6 +141,10 @@ class FileMetadata {

self.externalLyricsFile = persistentState.externalLyricsFile

if let lyricsDownloaded = persistentState.lyricsDownloaded {
self.lyricsDownloaded = lyricsDownloaded
}

self.nonEssentialMetadata = persistentState.nonEssentialMetadata

self.art = persistentCoverArt
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/TrackIO/Model/Track+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ extension Track {
}

var hasExternalLyrics: Bool {
metadata.externalLyricsFile != nil || metadata.externalTimedLyrics != nil
metadata.externalTimedLyrics != nil
}

// Non-essential metadata
Expand Down
22 changes: 22 additions & 0 deletions Source/Core/TrackIO/TrackInitializer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// TrackInitializer.swift
// Aural
//
// Copyright © 2025 Kartik Venugopal. All rights reserved.
//
// This software is licensed under the MIT software license.
// See the file "LICENSE" in the project root directory for license terms.
//

import Foundation

class TrackInitializer {


}

enum TrackInitPriority: Int {

case trackList = 0
case menu = 1
}
25 changes: 23 additions & 2 deletions Source/Core/TrackIO/TrackReader+Lyrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension TrackReader {

func loadExternalLyrics(for track: Track, immediate: Bool) {

guard !track.hasLyrics else {return}
guard !track.hasExternalLyrics else {return}

// Load lyrics from previously assigned external file
if let externalLyricsFile = track.metadata.externalLyricsFile, externalLyricsFile.exists,
Expand All @@ -43,7 +43,7 @@ extension TrackReader {
}

// Online search
guard onlineSearchEnabled, track.title != nil && track.artist != nil else {return}
guard !track.hasLyrics, onlineSearchEnabled, track.title != nil && track.artist != nil else {return}

Task.detached(priority: immediate ? .userInitiated : .utility) {

Expand All @@ -55,7 +55,9 @@ extension TrackReader {
Messenger.publish(TrackInfoUpdatedNotification(updatedTrack: track, updatedFields: .lyrics))

if let cachedLyricsFile = bestLyrics.persistToFile(track.defaultDisplayName) {

track.metadata.externalLyricsFile = cachedLyricsFile
track.metadata.lyricsDownloaded = true
}
}
}
Expand Down Expand Up @@ -132,7 +134,26 @@ extension TrackReader {
}

if let cachedLyricsFile = bestLyrics.persistToFile(track.defaultDisplayName) {

track.metadata.externalLyricsFile = cachedLyricsFile
track.metadata.lyricsDownloaded = true
}
}
}

func removeDownloadedLyrics(for track: Track) {

if let extLyricsFile = track.metadata.externalLyricsFile,
extLyricsFile.exists, extLyricsFile.parentDir == FilesAndPaths.lyricsDir {

extLyricsFile.moveToTrash()

track.metadata.externalLyricsFile = nil
track.metadata.externalTimedLyrics = nil
track.metadata.lyricsDownloaded = false

if appModeManager.isShowingLyrics || appModeManager.isShowingTrackInfo {
Messenger.publish(TrackInfoUpdatedNotification(updatedTrack: track, updatedFields: .lyrics, destructiveUpdate: true))
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions Source/Core/Utils/Extensions/URLExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,18 @@ extension URL {
}
}

func moveToTrash() {

guard exists else {return}

do {
try fileManager.trashItem(at: self, resultingItemURL: nil)

} catch let error as NSError {
NSLog("Error moving file '%@' to trash: %@", self.path, error.description)
}
}

// Renames this file
func rename(to target: URL) {

Expand Down
2 changes: 1 addition & 1 deletion Source/UI/Lyrics/Lyrics.xib
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
<rect key="frame" x="0.0" y="0.0" width="483" height="287"/>
<clipView key="contentView" id="n2f-37-6gY">
<rect key="frame" x="0.0" y="0.0" width="468" height="287"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="30" viewBased="YES" id="VEn-Ks-t68">
<rect key="frame" x="0.0" y="0.0" width="468" height="287"/>
Expand Down
4 changes: 4 additions & 0 deletions Source/UI/Lyrics/LyricsViewController+Timed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ extension LyricsViewController {
}

@IBAction func searchForLyricsOnlineButtonAction(_ sender: NSButton) {
searchForLyricsOnline()
}

func searchForLyricsOnline() {

guard let track else {return}

Expand Down
4 changes: 4 additions & 0 deletions Source/UI/Lyrics/LyricsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class LyricsViewController: NSViewController {
super.viewDidLoad()

view.wantsLayer = true

changeCornerRadius(to: playerUIState.cornerRadius)

fontSchemesManager.registerObserver(self)
Expand All @@ -68,6 +69,9 @@ class LyricsViewController: NSViewController {
})

messenger.subscribe(to: .Lyrics.loadFromFile, handler: loadLyrics(fromFile:))
messenger.subscribe(to: .Lyrics.searchForLyricsOnline, handler: searchForLyricsOnline, filter: {
appModeManager.isShowingLyrics
})
messenger.subscribe(to: .View.changeWindowCornerRadius, handler: changeCornerRadius(to:))
}

Expand Down
7 changes: 7 additions & 0 deletions Source/UI/Menus/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
<connections>
<outlet property="addLyricsFileMenuItem" destination="8BA-me-5uv" id="DGZ-yH-WHo"/>
<outlet property="detailedInfoMenuItem" destination="LJ6-A4-cet" id="fwF-TZ-Rr1"/>
<outlet property="removeOnlineLyricsMenuItem" destination="g1E-77-pB7" id="a5t-Rt-rYO"/>
<outlet property="searchForLyricsOnlineMenuItem" destination="4tR-6I-GhA" id="7cf-cD-jWE"/>
</connections>
</customObject>
Expand Down Expand Up @@ -807,6 +808,12 @@ CA
<action selector="searchForLyricsOnlineAction:" target="RL2-vF-Zyu" id="i9C-Gz-fcQ"/>
</connections>
</menuItem>
<menuItem title="Remove downloaded lyrics for playing track" image="xmark.bin" catalog="system" id="g1E-77-pB7">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="removeOnlineLyricsAction:" target="RL2-vF-Zyu" id="dcO-rU-QwY"/>
</connections>
</menuItem>
</items>
<connections>
<outlet property="delegate" destination="RL2-vF-Zyu" id="KNB-ha-E14"/>
Expand Down
18 changes: 18 additions & 0 deletions Source/UI/Menus/MetadataMenuController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,25 @@ class MetadataMenuController: NSObject, NSMenuDelegate {
@IBOutlet weak var detailedInfoMenuItem: NSMenuItem!
@IBOutlet weak var addLyricsFileMenuItem: NSMenuItem!
@IBOutlet weak var searchForLyricsOnlineMenuItem: NSMenuItem!
@IBOutlet weak var removeOnlineLyricsMenuItem: NSMenuItem!

func menuNeedsUpdate(_ menu: NSMenu) {

let isPlayingOrPaused = playbackInfoDelegate.state.isPlayingOrPaused
[detailedInfoMenuItem, addLyricsFileMenuItem, searchForLyricsOnlineMenuItem].forEach {$0?.enableIf(isPlayingOrPaused)}

var enableRemoveLyricsMenuItem = false

if let playingTrack = playbackInfoDelegate.playingTrack, playingTrack.metadata.lyricsDownloaded {

if let extLyricsFile = playingTrack.metadata.externalLyricsFile,
extLyricsFile.exists, extLyricsFile.parentDir == FilesAndPaths.lyricsDir {

enableRemoveLyricsMenuItem = true
}
}

removeOnlineLyricsMenuItem.showIf(enableRemoveLyricsMenuItem)
}

@IBAction func moreInfoAction(_ sender: AnyObject) {
Expand All @@ -33,4 +47,8 @@ class MetadataMenuController: NSObject, NSMenuDelegate {
@IBAction func searchForLyricsOnlineAction(_ sender: AnyObject) {
Messenger.publish(.Lyrics.searchForLyricsOnline)
}

@IBAction func removeOnlineLyricsAction(_ sender: AnyObject) {
Messenger.publish(.Lyrics.removeDownloadedLyrics)
}
}
3 changes: 2 additions & 1 deletion Source/UI/ModularPlayer/ModularPlayerWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class ModularPlayerWindowController: NSWindowController {
messenger.subscribeAsync(to: .Player.trackTransitioned, handler: trackTransitioned(notif:))

messenger.subscribeAsync(to: .Player.trackInfoUpdated, handler: lyricsLoaded(notif:), filter: {notif in
notif.updatedFields.contains(.lyrics)
notif.updatedFields.contains(.lyrics) && !notif.destructiveUpdate
})

colorSchemesManager.registerSchemeObserver(self)
Expand Down Expand Up @@ -168,6 +168,7 @@ class ModularPlayerWindowController: NSWindowController {

if preferences.metadataPreferences.lyrics.showWindowWhenPresent.value,
playbackInfoDelegate.playingTrack == notif.updatedTrack,
notif.updatedTrack.hasLyrics,
!appModeManager.isShowingLyrics {

windowLayoutsManager.showWindow(withId: .lyrics)
Expand Down
26 changes: 9 additions & 17 deletions Source/UI/Player/PlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ class PlayerViewController: NSViewController {

messenger.subscribe(to: .Lyrics.addLyricsFile, handler: addLyricsFile)
messenger.subscribe(to: .Lyrics.searchForLyricsOnline, handler: searchForLyricsOnline)
messenger.subscribe(to: .Lyrics.removeDownloadedLyrics, handler: removeDownloadedLyrics)
}

func previousTrack() {
Expand Down Expand Up @@ -579,26 +580,17 @@ class PlayerViewController: NSViewController {

func searchForLyricsOnline() {

guard let track = playbackInfoDelegate.playingTrack else {return}

guard track.title != nil || track.artist != nil else {
if !appModeManager.isShowingLyrics {

NSAlert.showError(withTitle: "Online lyrics search not possible", andText: "The playing track does not have artist/title metadata.")
return
}

let uiUpdateBlock = {(lyrics: TimedLyrics?) in

if lyrics != nil {
Messenger.publish(TrackInfoUpdatedNotification(updatedTrack: track, updatedFields: .lyrics))

} else {
NSAlert.showError(withTitle: "Lyrics not loaded", andText: "No lyrics found online for the playing track.")
}
Messenger.publish(.View.toggleLyrics)
Messenger.publish(.Lyrics.searchForLyricsOnline)
}
}

func removeDownloadedLyrics() {

Task.detached(priority: .userInitiated) {
await trackReader.searchForLyricsOnline(for: track, uiUpdateBlock: uiUpdateBlock)
if let playingTrack = playbackInfoDelegate.playingTrack {
trackReader.removeDownloadedLyrics(for: playingTrack)
}
}

Expand Down
3 changes: 2 additions & 1 deletion Source/UI/UnifiedPlayer/UnifiedPlayerWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class UnifiedPlayerWindowController: NSWindowController {
messenger.subscribe(to: .Player.trackTransitioned, handler: trackTransitioned(notif:))

messenger.subscribeAsync(to: .Player.trackInfoUpdated, handler: lyricsLoaded(notif:), filter: {notif in
notif.updatedFields.contains(.lyrics)
notif.updatedFields.contains(.lyrics) && !notif.destructiveUpdate
})

messenger.subscribe(to: .Application.willExit, handler: preApplicationExit)
Expand Down Expand Up @@ -329,6 +329,7 @@ class UnifiedPlayerWindowController: NSWindowController {

if preferences.metadataPreferences.lyrics.showWindowWhenPresent.value,
playbackInfoDelegate.playingTrack == notif.updatedTrack,
notif.updatedTrack.hasLyrics,
!appModeManager.isShowingLyrics {

showLyrics()
Expand Down

0 comments on commit 97d2d86

Please sign in to comment.