Archived
1

-Added Songtexte.com Lyrics Hoster

-Repositioned the load more results button into the outline view
-Improved the replacing of html escape characters
-Lyrics Hosters can now be dragged into a preferred order in the preference window
-Changed results outline's column ordering method
-Some code changes
-Replaced the Buttons in the lyrics pane with an action button
-Preferred order of lyrics hosters will now be saved
-Translation Improvements
This commit is contained in:
Kim Wittenburg
2012-06-24 14:22:37 +02:00
parent 98b0e70a8b
commit 41b1ef775c
27 changed files with 3611 additions and 2826 deletions

View File

@@ -64,4 +64,28 @@
-Fixed a bug: Returned false results when having a "&" in the query\
-Fixed a resource bug\
-Fixed special characters in url bug (final)\
}
\
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\qc
\i \cf0 06/23/2012\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
\i0 \cf0 -Added Songtexte.com Lyrics Hoster\
-Repositioned the load more results button into the outline view\
-Improved the replacing of html escape characters\
-Lyrics Hosters can now be dragged into a preferred order in the preference window\
-Changed results outline's column ordering method\
-Some code changes\
-Replaced the Buttons in the lyrics pane with an action button\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\b \cf0 Release No. 1.2
\b0 -
\i 06/24/2012\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
\i0 \cf0 -Preferred order of lyrics hosters will now be saved\
-Translation Improvements}

View File

@@ -1,78 +1,103 @@
/* Standards */
/* ---------- Standards - Werden in Warnmeldungen angezeigt ---------- */
"OK" = "OK";
"Yes" = "Ja";
"No" = "Nein";
/* "No Selection" label(s) in the main window */
/* ---------- iLyrics - Hauptfenster ---------- */
//----- Angezeigte Texte
/* Wird im PopUp Button im Ergebniss-Bereich angezeigt */
"iLyrics.text.loadNextResultsFrom" = "Weitere Ergebnisse laden von...";
/* "Keine Auswahl" - Texte im Hauptfenster */
"iLyrics.text.noSelection" = "Keine Auswahl";
/* Preference Items */
"iLyrics.text.enableAutoLyrics" = "Auto-Lyrics einschalten";
//----- Warnmeldungen
"iLyrics.text.disableAutoLyrics" = "Auto-Lyrics ausschalten";
//Songtextsuche (Aufgerufen, wenn der Benutzer im Suchfeld die Eingabe mit ↩ (Eingabetaste) bestätigt)
/* Text displayed when a lyrics hoster does not return a valid NSDate in it's hosterVersion method */
"iLyrics.text.illegalDateFormat" = "Keine Angabe";
/* Ein Netzwerkfehler ist aufgetreten, während die Ergebnisse geladen wurden */
"iLyrics.messages.networkOrQueryError.title" = "Sie sind nicht mit demInternet verbunden.";
"iLyrics.messages.networkOrQueryError.detail" = "Überprüfen Sie Ihre WLAN oder Ethernet Verbindung und versuchen Sie es erneut. Weitere Informationen erhalten Sie mit der Netzwerkdiagnose.";
/* Preferences */
/* Search Token Replacements */
"iLyrics.searchToken.%name%" = "Songname";
/* Die Suche ergab keine Ergebnisse */
"iLyrics.messages.noResults.title" = "Ihre Suche ergab keine Ergebnisse.";
"iLyrics.messages.noResults.detail" = "Keine der Songtextseiten bietet einen Songtext an, der zu Ihrer Suche passt.";
"iLyrics.searchToken.%artist%" = "Interpret";
/* Fehler beim Speichern eines Songtextes */
"iLyrics.messages.error.saveLyrics.title" = "Der Songtext konnte nicht gesichert werden.";
"iLyrics.messages.error.saveLyrics.detail" = "Prüfen Sie, ob Sie die Schreibberechtigung am ausgewählten Ort besitzen.";
"iLyrics.searchToken.%album%" = "Album";
/* Magistrix Hoster */
"Magistrix.text.noPreview" = "Keine Vorschau verfügbar.";
/* ---------- iLyrics - Einstellungsfenster ---------- */
"Hoster.text.noNetwork" = "Keine Netzwerkverbindung.";
//----- Angezeigte Texte
"Hoster.messages.networkOrQueryError.title" = "Sie sind zur Zeit nicht mit dem Internet verbunden oder haben einen ungültigen Suchtext eingegeben.";
/* Fenstertitel, wenn der Allgemein-Tab aktiv ist (Dies ist nicht die Beschriftung in der Symbolleiste) */
"iLyrics.preferences.text.general" = "Allgemein";
"Hoster.messages.networkOrQueryError.detail" = "1. Prüfen Sie, ob ihre WLAN oder Ethernet-Verbndung korrekt funktioniert. Weitere Informationenen erhalten Sie mit dem Netzwerkdiagnose Programm.\n2. Entfernen Sie alle Sonderzeichen (§$&€) aus dem Suchfeld und versuchen Sie es erneut.";
/* Fenstertitel, wenn der Auto-Lyrics-Tab aktiv ist (Dies ist nicht die Beschriftung in der Symbolleiste) */
"iLyrics.preferences.text.auto-lyrics" = "Auto-Lyrics";
/* Auto-Lyrics texte */
"iLyrics.preferences.text.enableAutoLyrics" = "Auto-Lyrics einschalten";
"iLyrics.preferences.text.disableAutoLyrics" = "Auto-Lyrics ausschalten";
/* ---------- Songtextseiten ---------- */
//----- Angezeigte Texte
/* Dies wird als Titel, Interpret und Songtexte angezeigt, wenn keine Internetverbindung besteht */
"Hoster.text.noNetwork" = "Keine Internetverbiindung.";
/* Die wird im Vorschaubereich angezeigt, wenn keine Vorschau erfügbar ist */
"Hoster.text.noPreviewAvailable" = "Keine Vorschau verfügbar.";
//----- Warnmeldungen (%hoster% wird mit dem Namen der Songtextseite ersetzt)
/* Es ist ein Netzwerkfehler aufgetreten, während ein Songtext geladen wurde */
"Hoster.messages.networkOrQueryError.title" = "Der Songtext konnte nicht geladen werden, da Sie nicht mit dem Internet verbunden sind.";
"Hoster.messages.networkOrQueryError.detail" = "Überprüfen Sie Ihre WLAN oder Ethernet Verbindung und versuchen Sie es erneut. Weitere Informationen erhalten Sie mit der Netzwerkdiagnose.";
/* (Veraltet - Dieser Text sollte nicht mehr verwendet werden, da er durch die hasMoreResults Methode des LyricsHoster-Protokolls hinfällig geworden ist) */
"Hoster.messages.noResults.title" = "Ihre Suche ergab keine Ergebnisse.";
"Hoster.messages.noResults.detail" = "%hoster% bietet leider keine Songtexte an, die zu Ihrer Suche passen.";
"Hoster.messages.noResults.detail" = "Magistrix.de hat keine Songtexte in der Datenbank, die mit der Suchanfrage übereinstimmen.";
/* Sollte niemals verwendet werden - Dies bedeutet, dass ein Fehler beim verarbeiten einer Seite aufgetreten ist. (Das kann vorkommen, wenn die Seiten aktualisiert werden und z.B. neue Features eingebaut werden.) */
"Magistrix.messages.unknownPage.title" = "Es ist ein Fehler beim Verarbeiten Ihrer Anfrage aufgetreten.";
"Magistrix.messages.unknownPage.detail" = "Normalerweise kann dieses Problem behoben werden, indem Sie die neuste Version von iLyrics herunterladen.";
"Magistrix.messages.unknownPage.title" = "Ein Fehler ist beim Verarbeiten der Anfrage aufgetreten.";
"Magistrix.messages.unknownPage.detail" = "Normalerweise kann dieses Problem behoben werden, wenn Sie die neuste Version von iLyrics laden.";
/* ---------- iTunes Verbindung ---------- */
/* iTunes connection */
/* By text used in the artist menu item where %@ is the artist */
"iTunes.text.byFormat" = "von %@";
//----- Warnmeldungen
/* iTunes (error) messages */
"iTunes.messages.iTunesIdle.title" = "iTunes ist zurzeit nicht geöffnet.";
/* Es wird kein Titel wiedergegeben */
"iTunes.messages.noTrackPlaying.title" = "iTunes spielt zur Zeit keine Musik.";
"iTunes.messages.noTrackPlaying.detail" = "Geben Sie einen Titel mit iTunes wieder oder wählen Sie \"Wiedergabe/Pause\" aus dem iTunes Menu, um diese Funktion zu verwenden.";
"iTunes.messages.iTunesIdle.detail" = "Öffnen Sie iTunes um dieses Feature nutzen zu können.";
/* Soll der gesicherte Songtext ersetzt werden */
"iTunes.messages.replaceLyrics.title" = "Der aktuelle iTunes Titel hat bereits einen Songtext. Möchten Sie diesen ersetzen?";
"iTunes.messages.replaceLyrics.detail" = "Der alte Songtext kann nicht wiederhergestellt werden.";
"iTunes.messages.noTrackPlaying.title" = "iTunes spielt zurzeit keine Musik.";
/* ---------- Spotify Verbindung ---------- */
"iTunes.messages.noTrackPlaying.detail" = "Starten sie die Wiedergabe über iTunes oder über den \"Play/Pause\" Button.";
/* Es wird kein Titel wiedergegeben */
"Spotify.messages.noTrackPlaying.title" = "Spotify spielt zur Zeit keine Musik.";
"Spotify.messages.noTrackPlaying.detail" = "Geben Sie einen Titel in Spotify wieder, um diese Funktion zu verwenden.";
"iTunes.messages.replaceLyrics.title" = "Der aktuelle iTunes Titel hat bereits einen Songtext. Möchten Sie ihn ersetzen?";
/* ---------- Growl Benachrichtigungen ---------- */
"iTunes.messages.replaceLyrics.detail" = "Beim Eretzen geht der alte Songtext vollständig verloren. Diese Aktion kann nicht wiederrufen werden.";
/* Spotify Messages */
"Spotify.messages.noTrackPlaying.title" = "Spotify spielt zut Zeit keine Musik.";
"Spotify.messages.noTrackPlaying.detail" = "Starten Sie die Wiedergabe in Spotify, um diese Funktion zu verwenden.";
/* Error while saving */
"messages.error.saveLyrics.title" = "Der Songtext konnte nicht gespeichert werden.";
"messages.error.saveLyrics.detail" = "Prüfen Sie, ob Sie Schreibberechtigung an diesem Ort haben.";
/* Growl */
"Growl.messages.lyricsSaved.title" = "Songtext gespeichert";
"Growl.messages.lyricsSaved.detail" = "nach \"%@\"";
/* Ein Songtext wurde erfolgreich gesichert (In der detailierten Beschreibung %@ ist der Name der Datei) */
"Growl.messages.lyricsSaved.title" = "Songtext gesichert";
"Growl.messages.lyricsSaved.detail" = "nach \"%@\".";
/* Ein Songtext wurde erfolgreich an iTunes übermittelt (In der detailierten Beschreibung %@ ist der Name des iTunes Titels) */
"Growl.messages.lyricsSent.title" = "Songtext an iTunes gesendet";
"Growl.messages.lyricsSent.detail" = "Songtext an \"%@\" gesendet.";

View File

@@ -1,79 +1,103 @@
/* Standards */
/* ---------- Standards - Used in Alert sheets and dialogs ---------- */
"OK" = "OK";
"Yes" = "Yes";
"No" = "No";
/* ---------- iLyrics - Main Window ---------- */
//----- Displayed Texts
/* Used in the NSPopUpButton inside the results outline */
"iLyrics.text.loadNextResultsFrom" = "Load more results from...";
/* "No Selection" label(s) in the main window */
"iLyrics.text.noSelection" = "No Selection";
/* Preference Items */
"iLyrics.text.enableAutoLyrics" = "Enable Auto-Lyrics";
//----- Alert Texts
"iLyrics.text.disableAutoLyrics" = "Disable Auto-Lyrics";
//Lyrics Search (Called when the user presses ↩ (Enter) on the search field)
/* Text displayed when a lyrics hoster does not return a valid NSDate in it's hosterVersion method */
"iLyrics.text.illegalDateFormat" = "No Specification";
/* A Network error occured while loading search results */
"iLyrics.messages.networkOrQueryError.title" = "You are not connected to the internet.";
"iLyrics.messages.networkOrQueryError.detail" = "Check if you are connected through WLAN or Ethernet. Use the Network Diagnostics for more information.";
/* Preferences */
/* Search Token Replacements */
"iLyrics.searchToken.%name%" = "Song Name";
"iLyrics.searchToken.%artist%" = "Artist";
"iLyrics.searchToken.%album%" = "Album";
/* Magistrix Hoster */
"Magistrix.text.noPreview" = "No Preview available.";
"Hoster.text.noNetwork" = "No Network connection";
"Hoster.messages.networkOrQueryError.title" = "You are not connected to the internet or you entered an invalid query.";
"Hoster.messages.networkOrQueryError.detail" = "1. Check if you are connected through WLAN or Ethernet. Use the Network Diagnostics for more information.\n2. Try to remove any special characters (&%$§€) and try again.";
"Hoster.messages.noResults.title" = "Your search did not return any results.";
"Hoster.messages.noResults.detail" = "Magistrix.de does not provide any lyrics for this search.";
"Magistrix.messages.unknownPage.title" = "There occured an error while parsing the request.";
"Magistrix.messages.unknownPage.detail" = "Normally this can be fixed by getting the newest versin of iLyrics.";
/* iTunes connection */
/* By text used in the artist menu item where %@ is the artist */
"iTunes.text.byFormat" = "by %@";
/* iTunes (error) messages */
"iTunes.messages.iTunesIdle.title" = "iTunes is idle.";
"iTunes.messages.iTunesIdle.detail" = "Please start iTunes to use this feature.";
"iTunes.messages.noTrackPlaying.title" = "iTunes does currently not play any music.";
"iTunes.messages.noTrackPlaying.detail" = "Play a track using iTunes or select \"Play/Pause\" from the iTunes menu.";
"iTunes.messages.replaceLyrics.title" = "The current iTunes track already has lyrics. Do you want to replace them?";
"iTunes.messages.replaceLyrics.detail" = "If you replace the old lyrics they can not be restored anymore.";
/* Spotify Messages */
"Spotify.messages.noTrackPlaying.title" = "Spotify does currently not play any music.";
"Spotify.messages.noTrackPlaying.detail" = "Play a track in Spotify to use this feature.";
/* The search did not return any results */
"iLyrics.messages.noResults.title" = "Your search did not return any results.";
"iLyrics.messages.noResults.detail" = "None of the hosters provides any songtexts matching to the entered query.";
/* Error while saving */
"messages.error.saveLyrics.title" = "The Lyrics could not be saved.";
"iLyrics.messages.error.saveLyrics.title" = "The Lyrics could not be saved.";
"iLyrics.messages.error.saveLyrics.detail" = "Ceck if you have the rights to write to the selected location.";
"messages.error.saveLyrics.detail" = "Ceck if you have the rights to write to the selected location.";
/* Growl */
/* ---------- iLyrics - Preference Window ---------- */
//----- Displayed Texts
/* Window title when the general tab is active (This is not the label for the toolbar item) */
"iLyrics.preferences.text.general" = "General";
/* Window title when the Auto-Lyrics tab is active (This is not the label for the toolbar item) */
"iLyrics.preferences.text.auto-lyrics" = "Auto-Lyrics";
/* Auto-Lyrics texts */
"iLyrics.preferences.text.enableAutoLyrics" = "Enable Auto-Lyrics";
"iLyrics.preferences.text.disableAutoLyrics" = "Disable Auto-Lyrics";
/* ---------- Lyrics Hosters ---------- */
//----- Displayed Texts
/* Displayed as song name, artist and lyrics when network connection failed */
"Hoster.text.noNetwork" = "No Network connection.";
/* Displayed in the preview area if no preview is available */
"Hoster.text.noPreviewAvailable" = "No Preview available.";
//----- Alert Texts (%hoster% will be replaced with the name of the hoster)
/* A Network error occured while loading a lyric */
"Hoster.messages.networkOrQueryError.title" = "You are not connected to the internet.";
"Hoster.messages.networkOrQueryError.detail" = "Check if you are connected through WLAN or Ethernet. Use the Network Diagnostics for more information.";
/* (Deprecated - Should not be used because of the hasMoreResults method of the LyricsHoster protocol) */
"Hoster.messages.noResults.title" = "Your search did not return any results.";
"Hoster.messages.noResults.detail" = "%hoster% does not provide any lyrics matching to the entered query.";
/* Should never be used - This means that a parsing error occured */
"Magistrix.messages.unknownPage.title" = "There occured an error while parsing the request.";
"Magistrix.messages.unknownPage.detail" = "Normally this can be fixed by getting the newest versin of iLyrics.";
/* ---------- iTunes Connection ---------- */
//----- Alert Texts
/* No Track is currently playing */
"iTunes.messages.noTrackPlaying.title" = "iTunes does currently not play any music.";
"iTunes.messages.noTrackPlaying.detail" = "Play a track using iTunes or select \"Play/Pause\" from the iTunes menu.";
/* Ask the user wether to replace the old lyrics */
"iTunes.messages.replaceLyrics.title" = "The current iTunes track already has lyrics. Do you want to replace them?";
"iTunes.messages.replaceLyrics.detail" = "If you replace the old lyrics they can not be restored anymore.";
/* ---------- Spotify Connection ---------- */
/* No Track is currentlx playing */
"Spotify.messages.noTrackPlaying.title" = "Spotify does currently not play any music.";
"Spotify.messages.noTrackPlaying.detail" = "Play a track in Spotify to use this feature.";
/* ---------- Growl Notifications ---------- */
/* A lyric was successfully saved to a file (in the detail message %@ is the filename) */
"Growl.messages.lyricsSaved.title" = "Lyrics Saved";
"Growl.messages.lyricsSaved.detail" = "to \"%@\".";
"Growl.messages.lyricsSaved.detail" = "to \"%@\"";
/* A lyric was successfully saved to the current iTunes song (in the detail message %@ is the name of the iTunes song) */
"Growl.messages.lyricsSent.title" = "Lyrics sent to iTunes";
/* %@ is the name of the song */
"Growl.messages.lyricsSent.detail" = "Lyrics sent to \"%@\".";

View File

@@ -12,7 +12,6 @@
3B20EF381584EA9A006C0CDF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20EF371584EA9A006C0CDF /* main.m */; };
3B20EF3C1584EA9A006C0CDF /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF3A1584EA9A006C0CDF /* Credits.rtf */; };
3B20EF3F1584EA9A006C0CDF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20EF3E1584EA9A006C0CDF /* AppDelegate.m */; };
3B20EF421584EA9A006C0CDF /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF401584EA9A006C0CDF /* MainMenu.xib */; };
3B20EF491584EAA3006C0CDF /* iLyrics.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF481584EAA3006C0CDF /* iLyrics.icns */; };
3B20EF4D1584EAAD006C0CDF /* iTunes.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF4A1584EAAD006C0CDF /* iTunes.icns */; };
3B20EF4E1584EAAD006C0CDF /* Diamond.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF4B1584EAAD006C0CDF /* Diamond.tiff */; };
@@ -20,11 +19,16 @@
3B20EF521584EAF8006C0CDF /* MainController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20EF511584EAF8006C0CDF /* MainController.m */; };
3B20EF551584ECD9006C0CDF /* Lyrics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20EF541584ECD9006C0CDF /* Lyrics.m */; };
3B20EF5C1584F34E006C0CDF /* SearchResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20EF5B1584F34E006C0CDF /* SearchResult.m */; };
3B20EF6115853345006C0CDF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3B20EF6315853345006C0CDF /* Localizable.strings */; };
3B2DC55B158CD31E00C004A4 /* PreferencesWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B2DC55A158CD31E00C004A4 /* PreferencesWindow.m */; };
3B3A377C15972079002085CE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B3A377E15972079002085CE /* MainMenu.xib */; };
3B3A3781159727FE002085CE /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3B3A3783159727FE002085CE /* Localizable.strings */; };
3B3A378715973040002085CE /* MP3Lyrics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A378615973040002085CE /* MP3Lyrics.m */; };
3B5E8BA2159496DA0028363E /* Songtexte.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E8BA1159496DA0028363E /* Songtexte.m */; };
3B5E8BA515949B380028363E /* LyricsHosterUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E8BA415949B380028363E /* LyricsHosterUtil.m */; };
3B5E8ED2158E7AC40082A769 /* Spotify.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3B5E8ED1158E7AC40082A769 /* Spotify.icns */; };
3B73010E158CDF7200D3AF26 /* AutoLyrics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B73010D158CDF7200D3AF26 /* AutoLyrics.m */; };
3B81D4FE1586248F00916CE3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B81D4FD1586248F00916CE3 /* ScriptingBridge.framework */; };
3B9A33611590BDE700B844EF /* iLyrics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B9A33601590BDE700B844EF /* iLyrics.m */; };
3BBD9B76158C873F00A5CD2C /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BBD9B75158C873F00A5CD2C /* Growl.framework */; };
3BBD9B78158C876200A5CD2C /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BBD9B75158C873F00A5CD2C /* Growl.framework */; };
3BBD9B7A158C87D000A5CD2C /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = 3BBD9B79158C87D000A5CD2C /* Growl Registration Ticket.growlRegDict */; };
@@ -58,7 +62,6 @@
3B20EF3B1584EA9A006C0CDF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
3B20EF3D1584EA9A006C0CDF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
3B20EF3E1584EA9A006C0CDF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
3B20EF411584EA9A006C0CDF /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
3B20EF481584EAA3006C0CDF /* iLyrics.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = iLyrics.icns; sourceTree = "<group>"; };
3B20EF4A1584EAAD006C0CDF /* iTunes.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = iTunes.icns; sourceTree = "<group>"; };
3B20EF4B1584EAAD006C0CDF /* Diamond.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Diamond.tiff; sourceTree = "<group>"; };
@@ -70,17 +73,27 @@
3B20EF5A1584F34E006C0CDF /* SearchResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchResult.h; sourceTree = "<group>"; };
3B20EF5B1584F34E006C0CDF /* SearchResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchResult.m; sourceTree = "<group>"; };
3B20EF5D1584F458006C0CDF /* LyricsHoster.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LyricsHoster.h; sourceTree = "<group>"; };
3B20EF6215853345006C0CDF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
3B2DC558158CCE8100C004A4 /* Changelog.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Changelog.rtf; sourceTree = SOURCE_ROOT; };
3B2DC559158CD31E00C004A4 /* PreferencesWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesWindow.h; sourceTree = "<group>"; };
3B2DC55A158CD31E00C004A4 /* PreferencesWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindow.m; sourceTree = "<group>"; };
3B3A377D15972079002085CE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
3B3A378015972080002085CE /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = "<group>"; };
3B3A3782159727FE002085CE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
3B3A378415972803002085CE /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
3B3A378515973040002085CE /* MP3Lyrics.h */ = {isa = PBXFileReference; fileEncoding = 4; path = MP3Lyrics.h; sourceTree = "<group>"; };
3B3A378615973040002085CE /* MP3Lyrics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP3Lyrics.m; sourceTree = "<group>"; };
3B5E8BA0159496DA0028363E /* Songtexte.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Songtexte.h; sourceTree = "<group>"; };
3B5E8BA1159496DA0028363E /* Songtexte.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Songtexte.m; sourceTree = "<group>"; };
3B5E8BA315949B380028363E /* LyricsHosterUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LyricsHosterUtil.h; sourceTree = "<group>"; };
3B5E8BA415949B380028363E /* LyricsHosterUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LyricsHosterUtil.m; sourceTree = "<group>"; };
3B5E8ECD158E788D0082A769 /* Spotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Spotify.h; sourceTree = "<group>"; };
3B5E8ED1158E7AC40082A769 /* Spotify.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Spotify.icns; sourceTree = "<group>"; };
3B73010C158CDF7200D3AF26 /* AutoLyrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoLyrics.h; sourceTree = "<group>"; };
3B73010D158CDF7200D3AF26 /* AutoLyrics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutoLyrics.m; sourceTree = "<group>"; };
3B81D4FD1586248F00916CE3 /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; };
3B81D5041586298100916CE3 /* iTunes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTunes.h; sourceTree = "<group>"; };
3BBD9B72158C7F8A00A5CD2C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
3B9A335F1590BDE700B844EF /* iLyrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iLyrics.h; sourceTree = "<group>"; };
3B9A33601590BDE700B844EF /* iLyrics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iLyrics.m; sourceTree = "<group>"; };
3BBD9B74158C86DA00A5CD2C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = de; path = de.lproj/Credits.rtf; sourceTree = "<group>"; };
3BBD9B75158C873F00A5CD2C /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Growl.framework; sourceTree = "<group>"; };
3BBD9B79158C87D000A5CD2C /* Growl Registration Ticket.growlRegDict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "Growl Registration Ticket.growlRegDict"; sourceTree = "<group>"; };
@@ -88,7 +101,6 @@
3BE95010158A269D00E78FEF /* Magistrix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Magistrix.m; sourceTree = "<group>"; };
3BE95015158A291500E78FEF /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesController.h; sourceTree = "<group>"; };
3BE95016158A291500E78FEF /* PreferencesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesController.m; sourceTree = "<group>"; };
3BFDD3FA158D420400BF32B0 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -148,15 +160,17 @@
isa = PBXGroup;
children = (
3B2DC558158CCE8100C004A4 /* Changelog.rtf */,
3B9A335F1590BDE700B844EF /* iLyrics.h */,
3B9A33601590BDE700B844EF /* iLyrics.m */,
3B73010C158CDF7200D3AF26 /* AutoLyrics.h */,
3B73010D158CDF7200D3AF26 /* AutoLyrics.m */,
3B20EF3D1584EA9A006C0CDF /* AppDelegate.h */,
3B20EF3E1584EA9A006C0CDF /* AppDelegate.m */,
3B20EF401584EA9A006C0CDF /* MainMenu.xib */,
3B3A377E15972079002085CE /* MainMenu.xib */,
3B2DC559158CD31E00C004A4 /* PreferencesWindow.h */,
3B2DC55A158CD31E00C004A4 /* PreferencesWindow.m */,
3BE95015158A291500E78FEF /* PreferencesController.h */,
3BE95016158A291500E78FEF /* PreferencesController.m */,
3B73010C158CDF7200D3AF26 /* AutoLyrics.h */,
3B73010D158CDF7200D3AF26 /* AutoLyrics.m */,
3B20EF501584EAF8006C0CDF /* MainController.h */,
3B20EF511584EAF8006C0CDF /* MainController.m */,
3B20EF5A1584F34E006C0CDF /* SearchResult.h */,
@@ -186,9 +200,15 @@
3B20EF591584F31D006C0CDF /* Lyrics Hoster */ = {
isa = PBXGroup;
children = (
3B20EF5D1584F458006C0CDF /* LyricsHoster.h */,
3BE9500F158A269D00E78FEF /* Magistrix.h */,
3BE95010158A269D00E78FEF /* Magistrix.m */,
3B20EF5D1584F458006C0CDF /* LyricsHoster.h */,
3B5E8BA0159496DA0028363E /* Songtexte.h */,
3B5E8BA1159496DA0028363E /* Songtexte.m */,
3B3A378515973040002085CE /* MP3Lyrics.h */,
3B3A378615973040002085CE /* MP3Lyrics.m */,
3B5E8BA315949B380028363E /* LyricsHosterUtil.h */,
3B5E8BA415949B380028363E /* LyricsHosterUtil.m */,
);
name = "Lyrics Hoster";
sourceTree = "<group>";
@@ -202,7 +222,7 @@
3B20EF4A1584EAAD006C0CDF /* iTunes.icns */,
3B20EF4B1584EAAD006C0CDF /* Diamond.tiff */,
3B20EF4C1584EAAD006C0CDF /* Download-icon.icns */,
3B20EF6315853345006C0CDF /* Localizable.strings */,
3B3A3783159727FE002085CE /* Localizable.strings */,
);
name = Resources;
sourceTree = "<group>";
@@ -261,12 +281,12 @@
files = (
3B20EF361584EA9A006C0CDF /* InfoPlist.strings in Resources */,
3B20EF3C1584EA9A006C0CDF /* Credits.rtf in Resources */,
3B20EF421584EA9A006C0CDF /* MainMenu.xib in Resources */,
3B3A377C15972079002085CE /* MainMenu.xib in Resources */,
3B20EF491584EAA3006C0CDF /* iLyrics.icns in Resources */,
3B20EF4D1584EAAD006C0CDF /* iTunes.icns in Resources */,
3B20EF4E1584EAAD006C0CDF /* Diamond.tiff in Resources */,
3B20EF4F1584EAAD006C0CDF /* Download-icon.icns in Resources */,
3B20EF6115853345006C0CDF /* Localizable.strings in Resources */,
3B3A3781159727FE002085CE /* Localizable.strings in Resources */,
3BBD9B7A158C87D000A5CD2C /* Growl Registration Ticket.growlRegDict in Resources */,
3B5E8ED2158E7AC40082A769 /* Spotify.icns in Resources */,
);
@@ -288,6 +308,10 @@
3BE95017158A291500E78FEF /* PreferencesController.m in Sources */,
3B2DC55B158CD31E00C004A4 /* PreferencesWindow.m in Sources */,
3B73010E158CDF7200D3AF26 /* AutoLyrics.m in Sources */,
3B9A33611590BDE700B844EF /* iLyrics.m in Sources */,
3B5E8BA2159496DA0028363E /* Songtexte.m in Sources */,
3B5E8BA515949B380028363E /* LyricsHosterUtil.m in Sources */,
3B3A378715973040002085CE /* MP3Lyrics.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -311,20 +335,20 @@
name = Credits.rtf;
sourceTree = "<group>";
};
3B20EF401584EA9A006C0CDF /* MainMenu.xib */ = {
3B3A377E15972079002085CE /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
3B20EF411584EA9A006C0CDF /* en */,
3BFDD3FA158D420400BF32B0 /* de */,
3B3A377D15972079002085CE /* en */,
3B3A378015972080002085CE /* de */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
3B20EF6315853345006C0CDF /* Localizable.strings */ = {
3B3A3783159727FE002085CE /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
3B20EF6215853345006C0CDF /* en */,
3BBD9B72158C7F8A00A5CD2C /* de */,
3B3A3782159727FE002085CE /* en */,
3B3A378415972803002085CE /* de */,
);
name = Localizable.strings;
sourceTree = "<group>";

View File

@@ -7,13 +7,11 @@
//
#import "AppDelegate.h"
#import "Magistrix.h"
@implementation AppDelegate {
iTunesApplication *iTunes;
NSArray *keyTokens;
NSMutableArray *searchFormat;
Magistrix *magistrix;
}
@synthesize window;
@@ -24,8 +22,6 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
magistrix = [[Magistrix alloc] init];
[preferencesController addHoster:magistrix];
keyTokens = [NSArray arrayWithObjects:@"%name%", @"%artist%", @"%album%", nil];
[window setExcludedFromWindowsMenu:YES];
iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
@@ -45,6 +41,7 @@
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:[quitWhenAllWindowClosedCheckBox state] == NSOnState forKey:@"Quit when all windows are closed"];
[mainController saveToDefalts:defaults];
[[iLyrics sharediLyrics] saveToDefaults:defaults];
[[AutoLyrics autoLyrics] saveToDefaults:defaults];
[defaults synchronize];
return NSTerminateNow;

View File

@@ -9,7 +9,8 @@
#import <Foundation/Foundation.h>
#import <Growl/Growl.h>
#import "iTunes.h"
#import "Magistrix.h"
#import "LyricsHoster.h"
#import "iLyrics.h"
@interface AutoLyrics : NSObject

View File

@@ -29,7 +29,7 @@ AutoLyrics *instace;
-(id)init {
enabled = NO;
replaceOldLyrics = NO;
[self setInterval:30];
[self setInterval:5];
return [super init];
}
@@ -57,7 +57,7 @@ AutoLyrics *instace;
-(void)shouldSetLyrics: (NSTimer *) sender {
if (enabled) {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
iTunesApplication *iTunes = [[iLyrics sharediLyrics] iTunes];
iTunesTrack *current = [iTunes currentTrack];
if ([current name] != nil) {
if (replaceOldLyrics || [[current lyrics] length] == 0) {
@@ -68,13 +68,13 @@ AutoLyrics *instace;
}
-(void)setLyrics {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
iTunesApplication *iTunes = [[iLyrics sharediLyrics] iTunes];
iTunesTrack *track = [iTunes currentTrack];
Magistrix *magistrix = [[Magistrix alloc] init];
[magistrix startNewSearchForQuery:[NSString stringWithFormat:@"%@ - %@", [track name], [track artist]]];
NSArray *results = [magistrix nextResults];
id<LyricsHoster> hoster = [[iLyrics sharediLyrics] preferredHoster];
[hoster startNewSearchForQuery:[NSString stringWithFormat:@"%@ - %@", [track name], [track artist]]];
NSArray *results = [hoster nextResults];
if (results != nil && [results count] > 0) {
Lyrics *lyrics = [magistrix lyricsBySearchResult:[results objectAtIndex:0]];
Lyrics *lyrics = [hoster lyricsBySearchResult:[results objectAtIndex:0]];
[track setLyrics:[lyrics lyrics]];
[GrowlApplicationBridge notifyWithTitle:NSLocalizedString(@"Growl.messages.lyricsSent.title", @"") description:[NSString stringWithFormat:NSLocalizedString(@"Growl.messages.lyricsSent.detail", @""), [track name]] notificationName:@"Lyrics sent to iTunes" iconData:nil priority:0 isSticky:NO clickContext:nil];

View File

@@ -7,11 +7,13 @@
//
#import <Foundation/Foundation.h>
#import "SearchResult.h"
#import "Lyrics.h"
#import "LyricsHosterUtil.h"
@protocol LyricsHoster <NSObject>
@property BOOL enabled;
-(NSString*) name;
-(NSDate*) hosterVersion;
@@ -25,7 +27,12 @@
-(void) resetLoadedResults;
-(BOOL) canShowInBrowser:(id)result;
-(void) showInBrowser:(id)result;
//Return nil for a "network error"
-(Lyrics*) lyricsBySearchResult: (SearchResult *) result;
//parameter should be a SearchResult instance
-(Lyrics*) lyricsBySearchResult: (id) result;
@end

View File

@@ -0,0 +1,14 @@
//
// LyricsHosterUtil.h
// iLyrics
//
// Created by Kim Wittenburg on 22.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString(LyricsHosterUtil)
-(NSString *) stringByRemovingHTMLTags;
-(NSString *) stringByFormattingForURL;
@end

View File

@@ -0,0 +1,23 @@
//
// LyricsHosterUtil.m
// iLyrics
//
// Created by Kim Wittenburg on 22.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "LyricsHosterUtil.h"
@implementation NSString(LyricsHosterUtil)
-(NSString*) stringByRemovingHTMLTags {
return [[[[[[[[[[[[[[[self
stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:@"<strong>" withString:@""] stringByReplacingOccurrencesOfString:@"</strong>" withString:@""] stringByReplacingOccurrencesOfString:@"<b>" withString:@""] stringByReplacingOccurrencesOfString:@"</b>" withString:@""] stringByReplacingOccurrencesOfString:@"<i>" withString:@""] stringByReplacingOccurrencesOfString:@"</i>" withString:@""] stringByReplacingOccurrencesOfString:@"<p>" withString:@""] stringByReplacingOccurrencesOfString:@"</p>" withString:@"\n\n"] stringByReplacingOccurrencesOfString:@"<br />" withString:@"\n"] stringByReplacingOccurrencesOfString:@"<br/>" withString:@"\n"] stringByReplacingOccurrencesOfString:@"&quot;" withString:@"\""] stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"] stringByReplacingOccurrencesOfString:@"&#039;" withString:@"'"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
-(NSString *) stringByFormattingForURL {
//Can replace äöü with aou, no difference in results
NSCharacterSet *characters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
return [[[self stringByReplacingOccurrencesOfString:@" " withString:@"+"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:characters];
}
@end

14
iLyrics/MP3Lyrics.h Normal file
View File

@@ -0,0 +1,14 @@
//
// Mp3Lyrics.h
// iLyrics
//
// Created by Kim Wittenburg on 24.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "LyricsHoster.h"
@interface MP3Lyrics : NSObject <LyricsHoster>
@end

68
iLyrics/MP3Lyrics.m Normal file
View File

@@ -0,0 +1,68 @@
//
// Mp3Lyrics.m
// iLyrics
//
// Created by Kim Wittenburg on 24.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "MP3Lyrics.h"
@implementation MP3Lyrics {
NSString *query;
NSString *firstPage;
int resultCount;
int loadedResults;
}
@synthesize enabled;
-(NSString *)name {
return @"MP3 Lyrics";
}
-(NSDate *)hosterVersion {
return [NSDate dateWithString:@"2012-06-24 15:00:00 +0100"];
}
-(void)startNewSearchForQuery:(NSString *)q {
query = [q stringByFormattingForURL];
int site = loadedResults;
NSString *searchPath = [NSString stringWithFormat:@"http://www.mp3lyrics.org/Search/%@%%7C%i", query, site];
NSURL *searchURL = [NSURL URLWithString:searchPath];
NSError *error;
firstPage = [NSString stringWithContentsOfURL:searchURL encoding:NSUTF8StringEncoding error:&error];
if (error || firstPage == nil) {
firstPage = nil;
return;
}
if ([firstPage rangeOfString:@"<div class=\"hit_list\">"].location == NSNotFound) {
resultCount = 0;
}
}
-(BOOL)hasMoreResults {
return loadedResults < resultCount;
}
-(NSArray *)nextResults {
}
-(BOOL)canShowInBrowser:(id)result {
}
-(void)showInBrowser:(id)result {
}
-(void)resetLoadedResults {
loadedResults = 0;
}
-(Lyrics *)lyricsBySearchResult:(id)result {
}
@end

View File

@@ -5,6 +5,7 @@
// Created by Kim Wittenburg on 14.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
// Representing http://www.magistrix.de
#import <Foundation/Foundation.h>
#import "LyricsHoster.h"

View File

@@ -6,9 +6,9 @@
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
//
//TODO: Use correct Date
#import "Magistrix.h"
#import "SearchResult.h"
typedef enum {
LyricsPage,
@@ -21,8 +21,11 @@ typedef enum {
NSString *query;
int loadedResults;
int resultCount;
NSString *firstPage;
}
@synthesize enabled;
-(NSString*) name {
return @"Magistrix";
}
@@ -34,6 +37,23 @@ typedef enum {
-(void) startNewSearchForQuery: (NSString*) q {
[self resetLoadedResults];
query = q;
int site = (loadedResults/10) + 1;
NSString *searchPath = [NSString stringWithFormat:@"http://www.magistrix.de/lyrics/search?q=%@&page=%i", [query stringByFormattingForURL], site];
NSURL *searchURL = [NSURL URLWithString:searchPath];
NSError *error;
firstPage = [NSString stringWithContentsOfURL:searchURL encoding:NSUTF8StringEncoding error:&error];
if (error || firstPage == nil) {
firstPage = nil;
return;
}
PageType type = [self typeOfPage:firstPage];
if (type == ResultsPage) {
[self shouldSetResultCountFromPage:firstPage];
} else if (type == LyricsPage) {
resultCount = 1;
} else {
resultCount = 0;
}
}
-(BOOL) hasMoreResults {
@@ -42,38 +62,33 @@ typedef enum {
-(NSArray*) nextResults {
int site = (loadedResults/10) + 1;
NSString *searchPath = [NSString stringWithFormat:@"http://www.magistrix.de/lyrics/search?q=%@&page=%i", [self stringByFormattingQuery:query], site];
NSString *searchPath = [NSString stringWithFormat:@"http://www.magistrix.de/lyrics/search?q=%@&page=%i", [query stringByFormattingForURL] , site];
NSURL *searchURL = [NSURL URLWithString:searchPath];
NSError *error;
NSString *page = [NSString stringWithContentsOfURL:searchURL encoding:NSUTF8StringEncoding error:&error];
if (error) {
//Network error or invalid query
return nil;
NSString *page;
if (site == 1 && firstPage != nil) {
page = firstPage;
} else {
NSError *error;
page = [NSString stringWithContentsOfURL:searchURL encoding:NSUTF8StringEncoding error:&error];
if (error) {
//Network error or invalid query
return nil;
}
}
PageType pType = [self typeOfPage:page];
if (pType == LyricsPage) {
resultCount = 1;
loadedResults = 1;
return [NSArray arrayWithObject:[self searchResultFromLyricsPage:page atURL:searchURL]];
} else if (pType == ResultsPage) {
[self shouldSetResultCountFromPage:page];
return [self searchResultsFromPage:page];
} else if (pType == NoResultsPage) {
resultCount = 0;
return [[NSArray alloc] init];
} else {
NSRunAlertPanel(NSLocalizedString(@"Magistrix.messages.unknownPage.title", @""), NSLocalizedString(@"Magistrix.messages.unknownPage.detail", @""), NSLocalizedString(@"OK", @""), nil, nil);
resultCount = 0;
return [[NSArray alloc] init];
}
}
-(NSString *) stringByFormattingQuery: (NSString *) q {
//Can replace äöü with aou, no difference in results
NSCharacterSet *characters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
return [[[q stringByReplacingOccurrencesOfString:@" " withString:@"+"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:characters];
}
-(PageType) typeOfPage: (NSString *) page {
if ([page rangeOfString:@"<title>Songtext-Suche</title>"].location != NSNotFound) {
if ([page rangeOfString:@"<div class='empty_collection'>"].location != NSNotFound) {
@@ -96,12 +111,12 @@ typedef enum {
int artistEnd = artistEndTag.location;
int songNameStart = NSMaxRange(artistEndTag);
int songNameEnd = headingEnd;
NSString *artist = [self stringByRemovingHTMLTags:[page substringWithRange:NSMakeRange(artistStart, artistEnd-artistStart)]];
NSString *artist = [[page substringWithRange:NSMakeRange(artistStart, artistEnd-artistStart)] stringByRemovingHTMLTags];
NSString *songName = [page substringWithRange:NSMakeRange(songNameStart, songNameEnd-songNameStart)];
//Remove the " Lyric" and the " &ndash; " from the Song name
songName = [self stringByRemovingHTMLTags:[[songName substringToIndex:[songName length]-[@" Lyric" length]] substringFromIndex:[@" &ndash; " length]]];
songName = [[[songName substringToIndex:[songName length]-[@" Lyric" length]] substringFromIndex:[@" &ndash; " length]] stringByRemovingHTMLTags];
NSString *preview = [self lyricsFromPage:page];
return [[SearchResult alloc]initWithName:songName fromArtist:artist preview:preview link:url];
return [[SearchResult alloc]initWithName:songName fromArtist:artist preview:preview link:url loadedByHoster:self];
}
-(NSArray*) searchResultsFromPage: (NSString *) page {
@@ -139,8 +154,7 @@ typedef enum {
NSRange artistStartRange = [tag rangeOfString:@">"];
int artistEndIndex = [tag rangeOfString:@"<" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:artistStartRange]].location;
int artistStartIndex = NSMaxRange(artistStartRange);
NSString *artist = [self stringByRemovingHTMLTags:[tag substringWithRange:NSMakeRange(artistStartIndex, artistEndIndex-artistStartIndex)]];
NSString *artist = [[tag substringWithRange:NSMakeRange(artistStartIndex, artistEndIndex-artistStartIndex)] stringByRemovingHTMLTags];
NSRange restRange = [self restRangeFromString:tag subtractingRange:NSMakeRange(artistEndIndex, [@"</a>" length])];
NSRange songNameTagStartRange = [tag rangeOfString:@"<a href=\"" options:NSCaseInsensitiveSearch range:restRange];
int linkStart = NSMaxRange(songNameTagStartRange);
@@ -148,12 +162,12 @@ typedef enum {
NSURL *link = [self urlFromHref:[tag substringWithRange:NSMakeRange(linkStart, linkEndRage.location-linkStart)]];
int songNameStart = NSMaxRange([tag rangeOfString:@">" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songNameTagStartRange]]);
int songNameEnd = [tag rangeOfString:@"</a>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songNameTagStartRange]].location;
NSString *songName = [self stringByRemovingHTMLTags:[tag substringWithRange:NSMakeRange(songNameStart, songNameEnd-songNameStart)]];
NSString *songName = [[tag substringWithRange:NSMakeRange(songNameStart, songNameEnd-songNameStart)] stringByRemovingHTMLTags];
int previewStart = songNameEnd + [@"</a>" length] + [@"\n<p>" length];
NSRange previewRestRange = NSMakeRange(previewStart, [tag length]-previewStart);
int previewEnd = [tag rangeOfString:@"</p>" options:NSCaseInsensitiveSearch range:previewRestRange].location;
NSString *preview = [self stringByRemovingHTMLTags:[tag substringWithRange:NSMakeRange(previewStart, previewEnd-previewStart)]];
return [[SearchResult alloc] initWithName:songName fromArtist:artist preview:preview link:link];
NSString *preview = [[tag substringWithRange:NSMakeRange(previewStart, previewEnd-previewStart)] stringByRemovingHTMLTags];
return [[SearchResult alloc] initWithName:songName fromArtist:artist preview:preview link:link loadedByHoster:self];
}
-(NSURL*) urlFromHref: (NSString *) link {
@@ -164,9 +178,6 @@ typedef enum {
}
}
-(NSString*) stringByRemovingHTMLTags: (NSString *)string {
return [[[[[[[[[[[string stringByReplacingOccurrencesOfString:@"<strong>" withString:@""] stringByReplacingOccurrencesOfString:@"</strong>" withString:@""] stringByReplacingOccurrencesOfString:@"<b>" withString:@""] stringByReplacingOccurrencesOfString:@"</b>" withString:@""] stringByReplacingOccurrencesOfString:@"<i>" withString:@""] stringByReplacingOccurrencesOfString:@"</i>" withString:@""] stringByReplacingOccurrencesOfString:@"<p>" withString:@""] stringByReplacingOccurrencesOfString:@"</p>" withString:@""] stringByReplacingOccurrencesOfString:@"<br />" withString:@""] stringByReplacingOccurrencesOfString:@"&quot;" withString:@"\""] stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"];
}
-(NSRange) restRangeFromString: (NSString *) page subtractingRange: (NSRange) aRange {
int loc = NSMaxRange(aRange);
@@ -174,7 +185,7 @@ typedef enum {
}
-(void) shouldSetResultCountFromPage: (NSString *) page {
if (resultCount == 0) {
if (resultCount <= 0) {
//Nothing loaded before
NSRange resultsLabelStartRange = [page rangeOfString:@"<p>Ergebnisse 1 bis "];
int resultsLabelEnd = [page rangeOfString:@"</p>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:page subtractingRange:resultsLabelStartRange]].location;
@@ -196,6 +207,7 @@ typedef enum {
-(void) resetLoadedResults {
loadedResults = 0;
firstPage = nil;
}
-(Lyrics*) lyricsBySearchResult: (SearchResult *) result {
@@ -208,13 +220,20 @@ typedef enum {
return [[Lyrics alloc] initWithName:[result name] byArtist:[result artist] withLyrics:lyrics];
}
-(NSString *) lyricsFromPage: (NSString *)page {
int lyricsStart = NSMaxRange([page rangeOfString:@"<div id='songtext'>"]) + [@"\n" length];
NSRange restRange = NSMakeRange(lyricsStart, [page length]-lyricsStart);
int lyricsEnd = [page rangeOfString:@"<div class='clear'></div>" options:NSCaseInsensitiveSearch range:restRange].location;
NSString *lyrics = [self stringByRemovingHTMLTags:[page substringWithRange:NSMakeRange(lyricsStart, lyricsEnd-lyricsStart)]];
NSString *lyrics = [[page substringWithRange:NSMakeRange(lyricsStart, lyricsEnd-lyricsStart)] stringByRemovingHTMLTags];
return lyrics;
}
-(BOOL)canShowInBrowser:(id)result {
return YES;
}
-(void)showInBrowser:(id)result {
[[NSWorkspace sharedWorkspace] openURL:[result link]];
}
@end

View File

@@ -6,37 +6,24 @@
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <Scripting/Scripting.h>
#import <ScriptingBridge/ScriptingBridge.h>
#import <AppKitScripting/AppKitScripting.h>
#import "iTunes.h"
#import "Spotify.h"
#import "Magistrix.h"
#import "SearchResult.h"
#import "Lyrics.h"
#import <Growl/Growl.h>
#import "iLyrics.h"
@interface MainController : NSObject <NSWindowDelegate, NSSplitViewDelegate, NSOutlineViewDataSource, NSOutlineViewDelegate>
@property (weak) IBOutlet NSMenuItem *iLyricsMenuItem;
@property (unsafe_unretained) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSSearchField *searchField;
@property (weak) IBOutlet NSOutlineView *resultsOutline;
@property (weak) IBOutlet NSButton *loadMoreResultsButton;
@property (weak) IBOutlet NSButton *showPreviewCheckBox;
@property (weak) IBOutlet NSPopover *previewPopover;
@property (unsafe_unretained) IBOutlet NSTextView *previewTextArea;
@property (weak) IBOutlet NSTextField *songLabel;
@property (weak) IBOutlet NSTextField *artistLabel;
@property (weak) IBOutlet NSButton *sendToiTunesButton;
@property (weak) IBOutlet NSButton *downloadLyricsButton;
@property (unsafe_unretained) IBOutlet NSTextView *lyricsArea;
@property (readonly) Lyrics *currentLyrics;
- (IBAction)getCurrentiTunesSong:(id)sender;
- (IBAction)getCurrentSpotifySong:(id)sender;
- (IBAction)startNewSearch:(id)sender;
- (IBAction)loadNextResults:(id)sender;
- (IBAction)resetLoadedResults:(id)sender;
- (IBAction)lyricsSelectionChanged:(NSOutlineView *)sender;
- (IBAction)sendLyricsToiTunes:(id)sender;

View File

@@ -10,46 +10,46 @@
@implementation MainController {
NSMutableArray *loadedResults;
id<LyricsHoster> currentHoster;
BOOL lyricsSelected;
NSInteger selectedSavePanelButton;
NSURL *saveFile;
iTunesApplication *iTunes;
SpotifyApplication *spotify;
Lyrics *currentLyrics;
int selectedRow;
iLyrics *ilyrics;
}
@synthesize iLyricsMenuItem;
@synthesize window;
@synthesize searchField;
@synthesize resultsOutline;
@synthesize loadMoreResultsButton;
@synthesize showPreviewCheckBox;
@synthesize previewPopover;
@synthesize previewTextArea;
@synthesize songLabel;
@synthesize artistLabel;
@synthesize sendToiTunesButton;
@synthesize downloadLyricsButton;
@synthesize lyricsArea;
-(id)init {
iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
spotify = [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
loadedResults = [[NSMutableArray alloc] init];
currentHoster = [[Magistrix alloc] init];
ilyrics = [iLyrics sharediLyrics];
return [super init];
}
-(void)awakeFromNib {
[self lyricsSelectionChanged:resultsOutline];
}
#pragma mark -
#pragma mark Outline view Data Source and Delegate
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return item == nil ? [loadedResults count] : 0;
return item == nil ? [loadedResults count] + 1 : 1;
}
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
if (index == [outlineView numberOfRows]-1) {
return NSLocalizedString(@"iLyrics.text.loadMoreResults", @"");
}
return [loadedResults objectAtIndex:index];
}
@@ -58,18 +58,41 @@
}
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
if ([item isKindOfClass:[NSString class]]) {
return item;
}
if ([[tableColumn identifier] isEqualToString:@"song"]) {
return [item name];
} else {
} else if ([[tableColumn identifier] isEqualToString:@"artist"]) {
return [item artist];
} else {
return [[item loadedByHoster] name];
}
}
-(NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([item isKindOfClass:[NSString class]]) {
NSPopUpButtonCell *cell = [[NSPopUpButtonCell alloc] init];
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *titleItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"iLyrics.text.loadNextResultsFrom", @"") action:@selector(nothingResponsible:soDisable:titleItem:) keyEquivalent:@""];
[menu addItem:titleItem];
for (id<LyricsHoster> hoster in [ilyrics lyricsHosters]) {
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", [hoster name]] action:@selector(loadNextResults:) keyEquivalent:@""];
[menuItem setTarget:self];
[menu addItem:menuItem];
}
[cell setMenu:menu];
[cell setControlSize:NSMiniControlSize];
return cell;
}
return nil;
}
-(NSString *)outlineView:(NSOutlineView *)outlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn item:(id)item mouseLocation:(NSPoint)mouseLocation {
//item is an instance of SearchResult
[self shouldShowPreviewForCellRect:*rect searchResult:item];
if ([item isKindOfClass:[SearchResult class]]) {
[self shouldShowPreviewForCellRect:*rect searchResult:item];
}
return nil;
}
@@ -77,11 +100,7 @@
#pragma mark Responding to Lyrics Search
- (IBAction)getCurrentiTunesSong:(id)sender {
iTunesTrack *track = [iTunes currentTrack];
if (track == nil) {
NSBeginAlertSheet(NSLocalizedString(@"iTunes.messages.iTunesIdle.title", @""), NSLocalizedString(@"OK", @""), nil, nil, window, nil, nil, nil, nil, NSLocalizedString(@"iTunes.messages.iTunesIdle.detail", @""));
return;
}
iTunesTrack *track = [[ilyrics iTunes] currentTrack];
NSString *name = [track name];
NSString *artist = [track artist];
if (name == nil) {
@@ -94,7 +113,7 @@
}
- (IBAction)getCurrentSpotifySong:(id)sender {
SpotifyTrack *track = [spotify currentTrack];
SpotifyTrack *track = [[ilyrics spotify] currentTrack];
NSString *name = [track name];
NSString *artist = [track artist];
if (name == nil) {
@@ -109,36 +128,97 @@
- (IBAction)startNewSearch:(id)sender {
[self resetLoadedResults:sender];
if ([[searchField stringValue] length] > 0) {
[currentHoster startNewSearchForQuery:[searchField stringValue]];
[self loadNextResults:sender];
for (id<LyricsHoster> hoster in [ilyrics lyricsHosters]) {
[hoster startNewSearchForQuery:[searchField stringValue]];
}
//Load first results
BOOL networkError = YES;
BOOL resultsFound = NO;
for (id<LyricsHoster> hoster in [ilyrics lyricsHosters]) {
NSArray *results = [hoster nextResults];
if (results) {
networkError = NO;
if ([results count] > 0) {
resultsFound = YES;
[loadedResults addObjectsFromArray:results];
[resultsOutline reloadData];
break;
}
} else {
networkError = networkError && YES;
}
}
if (networkError) {
[self presentNetworkErrorWithHoster:nil];
} else if (!resultsFound) {
[self presentNoResultsErrorWithHoster:nil];
}
}
[self lyricsSelectionChanged:resultsOutline];
}
- (IBAction)loadNextResults:(id)sender {
NSArray *nextResults = [currentHoster nextResults];
[loadMoreResultsButton setEnabled:[currentHoster hasMoreResults]];
- (void)loadNextResults:(id)sender{
[self loadNextResultsFromHoster:[ilyrics hosterWithName:[sender title]]];
}
- (void) loadNextResultsFromHoster: (id<LyricsHoster>) hoster {
if (hoster == nil) {
return;
}
NSArray *nextResults = [hoster nextResults];
if (nextResults == nil) {
NSRunCriticalAlertPanel(NSLocalizedString(@"Hoster.messages.networkOrQueryError.title", @""), NSLocalizedString(@"Hoster.messages.networkOrQueryError.detail", @""), NSLocalizedString(@"OK", @""), nil, nil);
[self presentNetworkErrorWithHoster:hoster];
return;
}
if ([nextResults count] == 0) {
NSRunAlertPanel(NSLocalizedString(@"Hoster.messages.noResults.title", @""), NSLocalizedString(@"Hoster.messages.noResults.detail", @""), NSLocalizedString(@"OK", @""), nil, nil);
[self presentNoResultsErrorWithHoster:hoster];
return;
}
[loadedResults addObjectsFromArray:nextResults];
[resultsOutline reloadData];
}
-(void)presentNetworkErrorWithHoster: (id<LyricsHoster>)hoster {
NSString *title;
NSString *detail;
if (hoster) {
title = [NSLocalizedString(@"Hoster.messages.networkOrQueryError.title", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[hoster name]];
detail = [NSLocalizedString(@"Hoster.messages.networkOrQueryError.detail", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[hoster name]];
} else {
title = NSLocalizedString(@"iLyrics.messages.networkOrQueryError.title", @"");
detail = NSLocalizedString(@"iLyrics.messages.networkOrQueryError.detail", @"");
}
NSRunCriticalAlertPanel(title, detail, NSLocalizedString(@"OK", @""), nil, nil);
}
-(void)presentNoResultsErrorWithHoster: (id<LyricsHoster>)hoster {
NSString *title;
NSString *detail;
if (hoster) {
title = [NSLocalizedString(@"Hoster.messages.noResults.title", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[hoster name]];
detail = [NSLocalizedString(@"Hoster.messages.noResults.detail", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[hoster name]];
} else {
title = NSLocalizedString(@"iLyrics.messages.noResults.title", @"") ;
detail = NSLocalizedString(@"iLyrics.messages.noResults.detail", @"");
}
NSRunAlertPanel(title, detail, NSLocalizedString(@"OK", @""), nil, nil);
}
-(IBAction)resetLoadedResults:(id)sender {
[currentHoster resetLoadedResults];
for (id<LyricsHoster> hoster in [ilyrics lyricsHosters]) {
[hoster resetLoadedResults];
}
[loadedResults removeAllObjects];
[resultsOutline reloadData];
[loadMoreResultsButton setEnabled:[currentHoster hasMoreResults]];
[self lyricsSelectionChanged:resultsOutline];
}
- (IBAction)lyricsSelectionChanged:(NSOutlineView *)sender {
int index = [sender selectedRow];
if (index == [sender numberOfRows]-1) {
index = -1;
}
if (index < 0) {
lyricsSelected = NO;
currentLyrics = nil;
@@ -151,10 +231,12 @@
if (selectedRow != index) {
lyricsSelected = YES;
SearchResult *result = [loadedResults objectAtIndex:index];
Lyrics *lyrics = [currentHoster lyricsBySearchResult:result];
Lyrics *lyrics = [[result loadedByHoster] lyricsBySearchResult:result];
currentLyrics = lyrics;
if (lyrics == nil) {
NSRunCriticalAlertPanel(NSLocalizedString(@"Hoster.messages.networkError.title", @""), NSLocalizedString(@"Hoster.messages.networkError.detail", @""), NSLocalizedString(@"OK", @""), nil, nil);
NSString *title = [NSLocalizedString(@"Hoster.messages.networkOrQueryError.title", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[[result loadedByHoster] name]];
NSString *detail = [NSLocalizedString(@"Hoster.messages.networkOrQueryError.detail", @"") stringByReplacingOccurrencesOfString:@"%hoster%" withString:[[result loadedByHoster] name]];
NSRunCriticalAlertPanel(title, detail, NSLocalizedString(@"OK", @""), nil, nil);
NSString *noNetwork = NSLocalizedString(@"Hoster.text.noNetwork", @"");
[songLabel setStringValue:noNetwork];
[artistLabel setStringValue:noNetwork];
@@ -176,14 +258,14 @@
NSString *lyrics = [result preview];
if (lyrics) {
rect.size.width = [resultsOutline frame].size.width;
[previewTextArea setString:lyrics];
[previewTextArea setString:lyrics==nil?NSLocalizedString(@"Hoster.text.noPreviewAvailable", @""):lyrics];
[previewPopover showRelativeToRect:rect ofView:resultsOutline preferredEdge:NSMaxXEdge];
}
}
}
- (IBAction)sendLyricsToiTunes:(id)sender {
iTunesTrack *track = [iTunes currentTrack];
iTunesTrack *track = [[ilyrics iTunes] currentTrack];
NSString *name = [track name];
if (name == nil) {
NSBeginAlertSheet(NSLocalizedString(@"iTunes.messages.noTrackPlaying.title", @""), NSLocalizedString(@"OK", @""), nil, nil, window, nil, nil, nil, nil, NSLocalizedString(@"iTunes.messages.noTrackPlaying.detail", @""));
@@ -199,7 +281,7 @@
- (void)replaceLyricsSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
if (returnCode == NSAlertDefaultReturn) {
iTunesTrack *track = [iTunes currentTrack];
iTunesTrack *track = [[ilyrics iTunes] currentTrack];
[track setLyrics:[lyricsArea string]];
[GrowlApplicationBridge notifyWithTitle:NSLocalizedString(@"Growl.messages.lyricsSent.title", @"") description:[NSString stringWithFormat:NSLocalizedString(@"Growl.messages.lyricsSent.detail", @""), [track name]] notificationName:@"Lyrics sent to iTunes" iconData:nil priority:0 isSticky:NO clickContext:nil];
}
@@ -225,8 +307,8 @@
if (row < 0) {
row = [resultsOutline selectedRow];
}
NSURL *link = [[loadedResults objectAtIndex:row] link];
[[NSWorkspace sharedWorkspace] openURL:link];
SearchResult *result = [loadedResults objectAtIndex:row];
[[result loadedByHoster] showInBrowser:result];
}
-(void) savePanelDidClose: (NSNotification *) notification{
@@ -234,7 +316,7 @@
if (selectedSavePanelButton == NSOKButton) {
BOOL success = [[[lyricsArea string] dataUsingEncoding:NSUTF8StringEncoding] writeToURL:saveFile atomically:NO];
if (!success) {
NSBeginAlertSheet(NSLocalizedString(@"messages.error.saveLyrics.title", @""), NSLocalizedString(@"OK", @""), nil, nil, window, nil, nil, nil, nil, NSLocalizedString(@"messages.error.saveLyrics.detail", @""));
NSBeginAlertSheet(NSLocalizedString(@"iLyrics.messages.error.saveLyrics.title", @""), NSLocalizedString(@"OK", @""), nil, nil, window, nil, nil, nil, nil, NSLocalizedString(@"iLyrics.messages.error.saveLyrics.detail", @""));
} else {
[GrowlApplicationBridge notifyWithTitle:NSLocalizedString(@"Growl.messages.lyricsSaved.title", @"") description:[NSString stringWithFormat:NSLocalizedString(@"Growl.messages.lyricsSaved.detail", @""), [saveFile path]] notificationName:@"Lyrics saved to File" iconData:nil priority:0 isSticky:NO clickContext:nil];
}
@@ -270,6 +352,11 @@
#pragma mark window delegate
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem {
SEL action = [menuItem action];
if (action == @selector(loadNextResults:)) {
BOOL hasMore = [[ilyrics hosterWithName:[menuItem title]] hasMoreResults];
return hasMore;
}
return [self validateUserInterfaceItem:menuItem];
}
@@ -280,28 +367,28 @@
-(BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
SEL action = [item action];
if (action == @selector(downloadLyrics:)) {
[downloadLyricsButton setEnabled:lyricsSelected];
return lyricsSelected;
}
if (action == @selector(sendLyricsToiTunes:)) {
BOOL enabled = lyricsSelected && [iTunes isRunning];
[sendToiTunesButton setEnabled:enabled];
BOOL enabled = lyricsSelected && [[ilyrics iTunes] isRunning];
return enabled;
}
if (action == @selector(getCurrentiTunesSong:)) {
return [iTunes isRunning];
return [[ilyrics iTunes] isRunning];
}
if (action == @selector(getCurrentSpotifySong:)) {
return [spotify isRunning];
return [[ilyrics spotify] isRunning];
}
if (action == @selector(showPreview:)) {
return [resultsOutline clickedRow] >= 0;
}
if (action == @selector(showLyricsInBrowser:)) {
if ([resultsOutline clickedRow] >= 0) {
return YES;
} else if ([resultsOutline selectedRow] >= 0) {
return YES;
SearchResult *result = [loadedResults objectAtIndex:[resultsOutline clickedRow]];
return [[result loadedByHoster] canShowInBrowser:result];;
} else if ([resultsOutline selectedRow] >= 0) {
SearchResult *result = [loadedResults objectAtIndex:[resultsOutline selectedRow]];
return [[result loadedByHoster] canShowInBrowser:result];;
} else {
return NO;
}

View File

@@ -11,20 +11,16 @@
#import "PreferencesWindow.h"
#import "AutoLyrics.h"
@interface PreferencesController : NSObject <NSWindowDelegate, NSTableViewDataSource>
@interface PreferencesController : NSObject <NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate>
@property (unsafe_unretained) IBOutlet PreferencesWindow *preferencesWindow;
@property (weak) IBOutlet NSToolbarItem *generalButton;
@property (weak) IBOutlet NSTableView *hosterTable;
@property NSArray *hosters;
@property (weak) IBOutlet NSView *generalView;
@property (weak) IBOutlet NSView *autoLyricsView;
@property (weak) IBOutlet NSButton *toggleAutoLyricsButton;
@property (weak) IBOutlet NSButton *replaceOldCheckBox;
-(void) addHoster: (id<LyricsHoster>) hoster;
-(void) removeHoster: (id<LyricsHoster>) hoster;
- (IBAction)showGeneralPreferences:(id)sender;
- (IBAction)showAutoLyricsPreferences:(id)sender;

View File

@@ -6,13 +6,14 @@
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
//TODO: Implement Auto-Lyricds Interval
//TODO: Set the title of the window when the toolbar button selection changed
//TODO:
#import "PreferencesController.h"
#import "iLyrics.h"
@implementation PreferencesController {
NSMutableArray *hosters;
AutoLyrics *autoLyrics;
iLyrics *ilyrics;
}
@synthesize generalView;
@synthesize autoLyricsView;
@@ -23,14 +24,15 @@
@synthesize hosterTable;
-(id)init {
ilyrics = [iLyrics sharediLyrics];
autoLyrics = [AutoLyrics autoLyrics];
hosters = [[NSMutableArray alloc] init];
return [super init];
}
#pragma mark -
#pragma mark Window Delegate Methods
#define HosterDataType @"HosterDataType"
-(void)awakeFromNib {
if ([autoLyrics enabled]) {
[self enableAutoLyrics:toggleAutoLyricsButton];
@@ -44,37 +46,19 @@
}
[[preferencesWindow toolbar] setSelectedItemIdentifier:@"general"];
[self showGeneralPreferences:nil];
[hosterTable registerForDraggedTypes:[NSArray arrayWithObject:HosterDataType]];
}
#pragma mark -
#pragma mark Properties
-(NSArray *)hosters {
return hosters;
}
-(void)setHosters:(NSArray *)hstrs {
hosters = [NSMutableArray arrayWithArray:hstrs];
[hosterTable reloadData];
}
#pragma mark Modifying hosters
-(void)addHoster:(id<LyricsHoster>)hoster {
[hosters addObject:hoster];
[hosterTable reloadData];
}
-(void)removeHoster:(id<LyricsHoster>)hoster {
[hosters removeObject:hoster];
[hosterTable reloadData];
}
- (IBAction)showGeneralPreferences:(id)sender {
[preferencesWindow setTitle:NSLocalizedString(@"iLyrics.preferences.text.general", @"")];
[self changeContentViewTo:generalView];
}
-(IBAction)showAutoLyricsPreferences:(id)sender {
[preferencesWindow setTitle:NSLocalizedString(@"iLyrics.preferences.text.auto-lyrics", @"")];
[self changeContentViewTo:autoLyricsView];
}
@@ -88,12 +72,12 @@
-(void)enableAutoLyrics: (id) sender{
[autoLyrics setEnabled:YES];
[sender setTitle:NSLocalizedString(@"iLyrics.text.disableAutoLyrics", @"")];
[sender setTitle:NSLocalizedString(@"iLyrics.preferences.text.disableAutoLyrics", @"")];
}
-(void)disableAutoLyrics: (id) sender {
[autoLyrics setEnabled:NO];
[sender setTitle:NSLocalizedString(@"iLyrics.text.enableAutoLyrics", @"")];
[sender setTitle:NSLocalizedString(@"iLyrics.preferences.text.enableAutoLyrics", @"")];
}
- (IBAction)changeAutoLyricsInterval:(id)sender {
@@ -112,18 +96,61 @@
#pragma mark -
#pragma mark Table Data Source
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [hosters count];
return [[ilyrics lyricsHosters] count];
}
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
id<LyricsHoster> hoster = [[ilyrics lyricsHosters] objectAtIndex:row];
if ([[tableColumn identifier] isEqualToString:@"hoster"]) {
return [[hosters objectAtIndex:row] name];
return [hoster name];
} else {
NSDate *version = [[hosters objectAtIndex:row] hosterVersion];
NSDate *version = [hoster hosterVersion];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
return [dateFormatter stringFromDate:version];
}
}
#pragma mark Drag and Drop Hosters
-(BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard {
// Copy the row numbers to the pasteboard.
NSData *indexData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:[NSArray arrayWithObject:HosterDataType] owner:self];
[pboard setData:indexData forType:HosterDataType];
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row
proposedDropOperation:(NSTableViewDropOperation)op {
// Add code here to validate the drop
return NSDragOperationMove;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation {
NSPasteboard* pboard = [info draggingPasteboard];
NSData* rowData = [pboard dataForType:HosterDataType];
NSIndexSet* rowIndexes =
[NSKeyedUnarchiver unarchiveObjectWithData:rowData];
NSInteger dragRow = [rowIndexes firstIndex];
if (dragRow < row) {
[[ilyrics lyricsHosters] insertObject:[[ilyrics lyricsHosters] objectAtIndex:dragRow] atIndex:row];
[[ilyrics lyricsHosters] removeObjectAtIndex:dragRow];
[hosterTable noteNumberOfRowsChanged];
[hosterTable reloadData];
return YES;
}
id<LyricsHoster> draggedHoster = [[ilyrics lyricsHosters] objectAtIndex:dragRow];
[[ilyrics lyricsHosters] removeObjectAtIndex:dragRow];
[[ilyrics lyricsHosters] insertObject:draggedHoster atIndex:row];
[hosterTable noteNumberOfRowsChanged];
[hosterTable reloadData];
return YES;
}
@end

View File

@@ -7,14 +7,16 @@
//
#import <Foundation/Foundation.h>
#import "LyricsHoster.h"
@interface SearchResult : NSObject
@property NSString *name;
@property NSString *artist;
@property NSString *preview;
@property id<LyricsHoster> loadedByHoster;
@property id link;
-(id)initWithName: (NSString*) name fromArtist: (NSString*) artist preview: (NSString*) preview link: (id) link;
-(id)initWithName: (NSString*) name fromArtist: (NSString*) artist preview: (NSString*) preview link: (id) link loadedByHoster: (id<LyricsHoster>) hoster;
@end

View File

@@ -12,13 +12,15 @@
@synthesize name;
@synthesize artist;
@synthesize preview;
@synthesize loadedByHoster;
@synthesize link;
-(id)initWithName:(NSString *)sng fromArtist:(NSString *)art preview:(NSString *)pre link:(id)l {
-(id)initWithName:(NSString *)sng fromArtist:(NSString *)art preview:(NSString *)pre link:(id)l loadedByHoster:(id<LyricsHoster>)hoster {
name = sng;
artist = art;
preview = pre;
link = l;
loadedByHoster = hoster;
return self;
}
@end

14
iLyrics/Songtexte.h Normal file
View File

@@ -0,0 +1,14 @@
//
// Songtexte.h
// iLyrics
//
// Created by Kim Wittenburg on 22.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
// Representing http://www.songtexte.com
#import <Foundation/Foundation.h>
#import "LyricsHoster.h"
@interface Songtexte : NSObject <LyricsHoster>
@end

153
iLyrics/Songtexte.m Normal file
View File

@@ -0,0 +1,153 @@
//
// Songtexte.m
// iLyrics
//
// Created by Kim Wittenburg on 22.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
// TODO: Implement parsing of multi-page results
#import "Songtexte.h"
#import "SearchResult.h"
@implementation Songtexte {
NSString *resultsPage;
BOOL hasMoreResults;
BOOL hadMoreResults;
}
@synthesize enabled;
-(NSString *) name {
return @"Songtexte.com";
}
-(NSDate *)hosterVersion {
return [NSDate dateWithString:@"2012-06-22 15:00:00 +0100"];
}
-(void) startNewSearchForQuery: (NSString *)query {
NSString *searchPath = [NSString stringWithFormat:@"http://www.songtexte.com/search?c=songs&q=%@", [query stringByFormattingForURL]];
NSURL *searchURL = [NSURL URLWithString:searchPath];
NSError *error;
NSString *page = [NSString stringWithContentsOfURL:searchURL encoding:NSUTF8StringEncoding error:&error];
if (error || page == nil) {
//Network or other error
resultsPage = nil;
hasMoreResults = hadMoreResults = NO;
}
resultsPage = page;
if ([resultsPage rangeOfString:@"</label>\n</div>\n</div>"].location == NSNotFound) {
//No Results
hasMoreResults = hadMoreResults = NO;
} else {
hasMoreResults = hadMoreResults = YES;
}
}
-(BOOL)hasMoreResults {
return hasMoreResults;
}
-(NSArray *)nextResults {
if (resultsPage == nil) {
//Network or other error
return nil;
}
NSRange tableStartRange = [resultsPage rangeOfString:@"</label>\n</div>\n</div>"];
if (tableStartRange.location == NSNotFound) {
//No Results
return [[NSArray alloc] init];
}
NSRange tableRange = [self restRangeFromString:resultsPage subtractingRange:tableStartRange];
int resultsTableStart = NSMaxRange([resultsPage rangeOfString:@"<ul>" options:NSCaseInsensitiveSearch range:tableRange]);
NSRange resultsTableEndRange = [resultsPage rangeOfString:@"<li id=\"" options:NSCaseInsensitiveSearch range:tableRange];
if (resultsTableEndRange.location == NSNotFound) {
resultsTableEndRange = [resultsPage rangeOfString:@"</ul>" options:NSCaseInsensitiveSearch range:tableRange];
}
int resultsTableEnd = resultsTableEndRange.location;
hasMoreResults = NO;
return [self resultsFromTable:[resultsPage substringWithRange:NSMakeRange(resultsTableStart, resultsTableEnd-resultsTableStart)]];
}
-(NSArray *)resultsFromTable:(NSString *)table {
NSRange restRange = NSMakeRange(0, [table length]);
NSMutableArray *tags = [[NSMutableArray alloc] init];
NSUInteger currentIndex = [table rangeOfString:@"<li>"].location;
while (currentIndex != NSNotFound) {
int startIndex = currentIndex + [@"<li>" length];
int endIndex = [table rangeOfString:@"</li>" options:NSCaseInsensitiveSearch range:restRange].location;
NSRange tagRange = NSMakeRange(startIndex, endIndex-startIndex);
SearchResult *result = [self searchResultFromTag:[table substringWithRange:tagRange]];
if (result) {
[tags addObject:result];
}
restRange = [self restRangeFromString:table subtractingRange:tagRange];
restRange.length -= [@"</li>" length];
restRange.location += [@"</li>" length];
currentIndex = [table rangeOfString:@"<li>" options:NSCaseInsensitiveSearch range:restRange].location;
}
return tags;
}
-(SearchResult *) searchResultFromTag: (NSString *)tag {
NSRange artistStartRange = [tag rangeOfString:@"Songtexte, Übersetzungen, Lyrics\"><span>"];
NSRange artistEndRange = [tag rangeOfString:@"</span>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:artistStartRange]];
int artistStart = NSMaxRange(artistStartRange);
NSString *artist = [[tag substringWithRange:NSMakeRange(artistStart, artistEndRange.location-artistStart)] stringByRemovingHTMLTags];
NSRange songLinkStartRange = [tag rangeOfString:@"<a" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:artistEndRange]];
/*if ([tag rangeOfString:@"title=\"zum Song (noch kein Songtext vorhanden)\"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songLinkStartRange]].location != NSNotFound) {
return nil;
}*/
//This would remove entries which does not exist anymore.
//The Problem: hasMoreResults woult also return YES if there is only one result (no matter if existing or not).
NSRange hrefStartRange = [tag rangeOfString:@"href=\"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songLinkStartRange]];
NSRange hrefEndRange = [tag rangeOfString:@"\"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:hrefStartRange]];
int hrefStart = NSMaxRange(hrefStartRange);
NSString *linkHref = [tag substringWithRange:NSMakeRange(hrefStart, hrefEndRange.location-hrefStart)];
NSRange nameStartRange = [tag rangeOfString:@"<span>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:hrefEndRange]];
NSRange nameEndRange = [tag rangeOfString:@"</span>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:nameStartRange]];
int nameStart = NSMaxRange(nameStartRange);
NSString *name = [[tag substringWithRange:NSMakeRange(nameStart, nameEndRange.location-nameStart)] stringByRemovingHTMLTags];
return [[SearchResult alloc] initWithName:name fromArtist:artist preview:nil link:[self urlFromHref:linkHref] loadedByHoster:self];
}
-(NSURL*) urlFromHref: (NSString *) link {
if (![link hasPrefix:@"http://www.songtexte.com"]) {
return [NSURL URLWithString:[NSString stringWithFormat:@"http://www.songtexte.com/%@", link]];
} else {
return [NSURL URLWithString:link];
}
}
-(NSRange) restRangeFromString: (NSString *) page subtractingRange: (NSRange) aRange {
int loc = NSMaxRange(aRange);
return NSMakeRange(loc, [page length]-loc);
}
-(void)resetLoadedResults {
hasMoreResults = hadMoreResults;
}
-(Lyrics *)lyricsBySearchResult:(id)result {
NSError *error;
NSString *page = [NSString stringWithContentsOfURL:[result link] encoding:NSUTF8StringEncoding error:&error];
if (error || page == nil) {
return nil;
}
NSRange lyricsStartRange = [page rangeOfString:@"<div id=\"lyrics\">"];
NSRange lyricsEndRange = [page rangeOfString:@"</div>" options:NSCaseInsensitiveSearch range:[self restRangeFromString:page subtractingRange:lyricsStartRange]];
int lyricsStart = NSMaxRange(lyricsStartRange);
NSString *lyrics = [[page substringWithRange:NSMakeRange(lyricsStart, lyricsEndRange.location-lyricsStart)] stringByRemovingHTMLTags];
return [[Lyrics alloc] initWithName:[result name] byArtist:[result artist] withLyrics:lyrics];
}
-(BOOL)canShowInBrowser:(id)result {
return YES;
}
-(void)showInBrowser:(id)result {
[[NSWorkspace sharedWorkspace] openURL:[result link]];
}
@end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.1.1</string>
<string>1.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>10</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.music</string>
<key>LSMinimumSystemVersion</key>

38
iLyrics/iLyrics.h Normal file
View File

@@ -0,0 +1,38 @@
//
// iLyrics.h
// iLyrics
//
// Created by Kim Wittenburg on 19.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <Growl/Growl.h>
#import "LyricsHoster.h"
#import "Magistrix.h"
#import "Songtexte.h"
#import "MP3Lyrics.h"
#import "SearchResult.h"
#import "Lyrics.h"
#import "iTunes.h"
#import "Spotify.h"
@interface iLyrics : NSObject
@property NSMutableArray *lyricsHosters;
@property(readonly) id<LyricsHoster> preferredHoster;
@property(readonly) Magistrix *magistrix;
@property(readonly) Songtexte *songtexte;
@property(readonly) MP3Lyrics *mp3Lyrics;
@property(readonly) iTunesApplication *iTunes;
@property(readonly) SpotifyApplication *spotify;
+(iLyrics *)sharediLyrics;
-(id<LyricsHoster>) hosterWithName: (NSString *) name;
-(void)saveToDefaults:(NSUserDefaults *)defaults;
-(void)loadFromDefaults:(NSUserDefaults *)defaults;
@end

78
iLyrics/iLyrics.m Normal file
View File

@@ -0,0 +1,78 @@
//
// iLyrics.m
// iLyrics
//
// Created by Kim Wittenburg on 19.06.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "iLyrics.h"
@implementation iLyrics
iLyrics *ilyrics;
@synthesize lyricsHosters;
@synthesize magistrix;
@synthesize songtexte;
@synthesize mp3Lyrics;
@synthesize iTunes;
@synthesize spotify;
+(iLyrics *)sharediLyrics {
if (ilyrics == nil) {
ilyrics = [[iLyrics alloc] init];
}
return ilyrics;
}
-(id)init {
self = [super init];
if (self) {
iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
spotify = [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
magistrix = [[Magistrix alloc] init];
songtexte = [[Songtexte alloc] init];
//mp3Lyrics = [[MP3Lyrics alloc] init];
lyricsHosters = [NSMutableArray arrayWithObjects:magistrix, songtexte, /*mp3Lyrics,*/ nil];
[self loadFromDefaults:[NSUserDefaults standardUserDefaults]];
}
return self;
}
-(void)loadFromDefaults:(NSUserDefaults *)defaults {
NSArray *hosterNames = [defaults objectForKey:@"Lyrics Hosters"];
NSMutableArray *reorderedHosters = [[NSMutableArray alloc] init];
for (NSString *name in hosterNames) {
id<LyricsHoster> hoster = [self hosterWithName:name];
if (hoster) {
[reorderedHosters addObject:hoster];
}
}
if ([reorderedHosters count] == [lyricsHosters count]) {
lyricsHosters = reorderedHosters;
}
}
-(id<LyricsHoster>)preferredHoster {
return [lyricsHosters objectAtIndex:0];
}
-(id<LyricsHoster>)hosterWithName:(NSString *)name {
for (id<LyricsHoster> hoster in lyricsHosters) {
if ([name isEqualToString:[hoster name]]) {
return hoster;
}
}
return nil;
}
-(void)saveToDefaults:(NSUserDefaults *)defaults {
NSMutableArray *hosterNames = [[NSMutableArray alloc] init];
for (id<LyricsHoster> hoster in lyricsHosters) {
[hosterNames addObject:[hoster name]];
}
[defaults setObject:hosterNames forKey:@"Lyrics Hosters"];
}
@end