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

@@ -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) {
@@ -67,14 +67,14 @@ AutoLyrics *instace;
}
}
-(void)setLyrics {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
-(void)setLyrics {
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