-Changed the maximum width of the search field -Fixed a bug: "Network error" when having special characters in query -Fixed a bug: Returned false results when having a "&" in the query
221 lines
11 KiB
Objective-C
221 lines
11 KiB
Objective-C
//
|
|
// Magistrix.m
|
|
// iLyrics
|
|
//
|
|
// Created by Kim Wittenburg on 14.06.12.
|
|
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
|
|
//
|
|
//
|
|
//TODO: Use correct Date
|
|
|
|
#import "Magistrix.h"
|
|
|
|
typedef enum {
|
|
LyricsPage,
|
|
ResultsPage,
|
|
NoResultsPage,
|
|
UnknownPage
|
|
} PageType;
|
|
|
|
@implementation Magistrix {
|
|
NSString *query;
|
|
int loadedResults;
|
|
int resultCount;
|
|
}
|
|
|
|
-(NSString*) name {
|
|
return @"Magistrix";
|
|
}
|
|
|
|
-(NSDate *)hosterVersion {
|
|
return [NSDate dateWithString:@"2012-06-16 11:00:00 +0100"];
|
|
}
|
|
|
|
-(void) startNewSearchForQuery: (NSString*) q {
|
|
[self resetLoadedResults];
|
|
query = q;
|
|
}
|
|
|
|
-(BOOL) hasMoreResults {
|
|
return loadedResults < resultCount;
|
|
}
|
|
|
|
-(NSArray*) nextResults {
|
|
int site = (loadedResults/10) + 1;
|
|
NSString *searchPath = [NSString stringWithFormat:@"http://www.magistrix.de/lyrics/search?q=%@&page=%i", [self stringByFormattingQuery:query], 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;
|
|
}
|
|
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 {
|
|
NSRange stringRange = NSMakeRange(0, [q length]);
|
|
//Can replace äöü with aou, no difference in results
|
|
NSCharacterSet *characters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
|
|
return [[[[[[q stringByReplacingOccurrencesOfString:@" " withString:@"+"] stringByReplacingOccurrencesOfString:@"Ö" withString:@"o" options:NSCaseInsensitiveSearch range:stringRange] stringByReplacingOccurrencesOfString:@"Ä" withString:@"a" options:NSCaseInsensitiveSearch range:stringRange] stringByReplacingOccurrencesOfString:@"Ü" withString:@"ü" options:NSCaseInsensitiveSearch range:stringRange] stringByReplacingOccurrencesOfString:@"&" withString:@"%26"] stringByTrimmingCharactersInSet:characters];
|
|
}
|
|
|
|
-(PageType) typeOfPage: (NSString *) page {
|
|
if ([page rangeOfString:@"<title>Songtext-Suche</title>"].location != NSNotFound) {
|
|
if ([page rangeOfString:@"<div class='empty_collection'>"].location != NSNotFound) {
|
|
return NoResultsPage;
|
|
} else {
|
|
return ResultsPage;
|
|
}
|
|
}
|
|
if ([page rangeOfString:@"<div id='songtext'>"].location != NSNotFound) {
|
|
return LyricsPage;
|
|
}
|
|
return UnknownPage;
|
|
}
|
|
|
|
-(SearchResult*) searchResultFromLyricsPage: (NSString *) page atURL: (NSURL *) url{
|
|
int headingStart = NSMaxRange([page rangeOfString:@"<h1>"]);
|
|
int headingEnd = [page rangeOfString:@"</h1>"].location;
|
|
int artistStart = NSMaxRange([page rangeOfString:@">" options:NSCaseInsensitiveSearch range:NSMakeRange(headingStart, headingEnd-headingStart)]);
|
|
NSRange artistEndTag = [page rangeOfString:@"</a>" options:NSCaseInsensitiveSearch range:NSMakeRange(artistStart, headingEnd-artistStart)];
|
|
int artistEnd = artistEndTag.location;
|
|
int songNameStart = NSMaxRange(artistEndTag);
|
|
int songNameEnd = headingEnd;
|
|
NSString *artist = [self stringByRemovingHTMLTags:[page substringWithRange:NSMakeRange(artistStart, artistEnd-artistStart)]];
|
|
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]]];
|
|
NSString *preview = [self lyricsFromPage:page];
|
|
return [[SearchResult alloc]initWithName:songName fromArtist:artist preview:preview link:url];
|
|
}
|
|
|
|
-(NSArray*) searchResultsFromPage: (NSString *) page {
|
|
int resultsTableStart = NSMaxRange([page rangeOfString:@"<table>"]);
|
|
int resultsTableEnd = [page rangeOfString:@"</table>"].location;
|
|
NSArray *resultTags = [self resultTagsFromTable:[page substringWithRange:NSMakeRange(resultsTableStart, resultsTableEnd-resultsTableStart)]];
|
|
NSMutableArray *searchResults = [[NSMutableArray alloc] init];
|
|
for (NSString *tag in resultTags) {
|
|
[searchResults addObject:[self searchResultFromResultTag:tag]];
|
|
}
|
|
//Increase loadedResults by 10 which is a full results site
|
|
//Even if there are less than 10 results on a page (would not have any effect)
|
|
loadedResults += 10;
|
|
return searchResults;
|
|
}
|
|
|
|
-(NSArray*) resultTagsFromTable: (NSString *) table {
|
|
NSRange restRange = NSMakeRange(0, [table length]);
|
|
NSMutableArray *tags = [[NSMutableArray alloc] init];
|
|
NSUInteger currentIndex = [table rangeOfString:@"<td>"].location;
|
|
while (currentIndex != NSNotFound) {
|
|
int startIndex = currentIndex + [@"<td>" length];
|
|
int endIndex = [table rangeOfString:@"</td>" options:NSCaseInsensitiveSearch range:restRange].location;
|
|
NSRange tagRange = NSMakeRange(startIndex, endIndex-startIndex);
|
|
[tags addObject:[table substringWithRange:tagRange]];
|
|
restRange = [self restRangeFromString:table subtractingRange:tagRange];
|
|
restRange.length -= [@"</td>" length];
|
|
restRange.location += [@"</td>" length];
|
|
currentIndex = [table rangeOfString:@"<td>" options:NSCaseInsensitiveSearch range:restRange].location;
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
-(SearchResult*) searchResultFromResultTag: (NSString *) tag {
|
|
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)]];
|
|
|
|
NSRange restRange = [self restRangeFromString:tag subtractingRange:NSMakeRange(artistEndIndex, [@"</a>" length])];
|
|
NSRange songNameTagStartRange = [tag rangeOfString:@"<a href=\"" options:NSCaseInsensitiveSearch range:restRange];
|
|
int linkStart = NSMaxRange(songNameTagStartRange);
|
|
NSRange linkEndRage = [tag rangeOfString:@"\"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songNameTagStartRange]];
|
|
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)]];
|
|
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];
|
|
}
|
|
|
|
-(NSURL*) urlFromHref: (NSString *) link {
|
|
if ([link hasPrefix:@"/"]) {
|
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://www.Magistrix.de%@", link]];
|
|
} else {
|
|
return [NSURL URLWithString:link];
|
|
}
|
|
}
|
|
|
|
-(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);
|
|
return NSMakeRange(loc, [page length]-loc);
|
|
}
|
|
|
|
-(void) shouldSetResultCountFromPage: (NSString *) page {
|
|
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;
|
|
NSString *resultsLabel = [page substringWithRange:NSMakeRange(NSMaxRange(resultsLabelStartRange), resultsLabelEnd-resultsLabelStartRange.location)];
|
|
if ([resultsLabel rangeOfString:@"ungefähr"].location != NSNotFound){
|
|
//Needs not more than 100 which means 10 sites (which is the maximum)
|
|
resultCount = 100;
|
|
} else {
|
|
int resultsStart = NSMaxRange([resultsLabel rangeOfString:@"von "]);
|
|
int resultsEnd = [resultsLabel rangeOfString:@"<"].location;
|
|
NSString *results = [resultsLabel substringWithRange:NSMakeRange(resultsStart, resultsEnd-resultsStart)];
|
|
resultCount = [results intValue];
|
|
}
|
|
if (resultCount > 100) {
|
|
resultCount = 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void) resetLoadedResults {
|
|
loadedResults = 0;
|
|
}
|
|
|
|
-(Lyrics*) lyricsBySearchResult: (SearchResult *) result {
|
|
NSError *error = nil;
|
|
NSString *page = [NSString stringWithContentsOfURL:[result link] encoding:NSUTF8StringEncoding error:&error];
|
|
if (error) {
|
|
return nil;
|
|
}
|
|
NSString *lyrics = [self lyricsFromPage:page];
|
|
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)]];
|
|
return lyrics;
|
|
}
|
|
@end
|