-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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
14
iLyrics/LyricsHosterUtil.h
Normal file
14
iLyrics/LyricsHosterUtil.h
Normal 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
|
||||
23
iLyrics/LyricsHosterUtil.m
Normal file
23
iLyrics/LyricsHosterUtil.m
Normal 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:@""" withString:@"\""] stringByReplacingOccurrencesOfString:@"&" withString:@"&"] stringByReplacingOccurrencesOfString:@"'" 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
14
iLyrics/MP3Lyrics.h
Normal 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
68
iLyrics/MP3Lyrics.m
Normal 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 " – " from the Song name
|
||||
songName = [self stringByRemovingHTMLTags:[[songName substringToIndex:[songName length]-[@" Lyric" length]] substringFromIndex:[@" – " length]]];
|
||||
songName = [[[songName substringToIndex:[songName length]-[@" Lyric" length]] substringFromIndex:[@" – " 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:@""" withString:@"\""] stringByReplacingOccurrencesOfString:@"&" 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
14
iLyrics/Songtexte.h
Normal 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
153
iLyrics/Songtexte.m
Normal 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
@@ -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
38
iLyrics/iLyrics.h
Normal 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
78
iLyrics/iLyrics.m
Normal 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
|
||||
Reference in New Issue
Block a user