// // 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:@"Songtext-Suche"].location != NSNotFound) { if ([page rangeOfString:@"
"].location != NSNotFound) { return NoResultsPage; } else { return ResultsPage; } } if ([page rangeOfString:@"
"].location != NSNotFound) { return LyricsPage; } return UnknownPage; } -(SearchResult*) searchResultFromLyricsPage: (NSString *) page atURL: (NSURL *) url{ int headingStart = NSMaxRange([page rangeOfString:@"

"]); int headingEnd = [page rangeOfString:@"

"].location; int artistStart = NSMaxRange([page rangeOfString:@">" options:NSCaseInsensitiveSearch range:NSMakeRange(headingStart, headingEnd-headingStart)]); NSRange artistEndTag = [page rangeOfString:@"" 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:@""]); int resultsTableEnd = [page rangeOfString:@"
"].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:@""].location; while (currentIndex != NSNotFound) { int startIndex = currentIndex + [@"" length]; int endIndex = [table rangeOfString:@"" options:NSCaseInsensitiveSearch range:restRange].location; NSRange tagRange = NSMakeRange(startIndex, endIndex-startIndex); [tags addObject:[table substringWithRange:tagRange]]; restRange = [self restRangeFromString:table subtractingRange:tagRange]; restRange.length -= [@"" length]; restRange.location += [@"" length]; currentIndex = [table rangeOfString:@"" 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, [@"" length])]; NSRange songNameTagStartRange = [tag rangeOfString:@"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songNameTagStartRange]]); int songNameEnd = [tag rangeOfString:@"" options:NSCaseInsensitiveSearch range:[self restRangeFromString:tag subtractingRange:songNameTagStartRange]].location; NSString *songName = [self stringByRemovingHTMLTags:[tag substringWithRange:NSMakeRange(songNameStart, songNameEnd-songNameStart)]]; int previewStart = songNameEnd + [@"" length] + [@"\n

" length]; NSRange previewRestRange = NSMakeRange(previewStart, [tag length]-previewStart); int previewEnd = [tag rangeOfString:@"

" 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:@"" withString:@""] stringByReplacingOccurrencesOfString:@"" withString:@""] stringByReplacingOccurrencesOfString:@"" withString:@""] stringByReplacingOccurrencesOfString:@"" withString:@""] stringByReplacingOccurrencesOfString:@"" withString:@""] stringByReplacingOccurrencesOfString:@"" withString:@""] stringByReplacingOccurrencesOfString:@"

" withString:@""] stringByReplacingOccurrencesOfString:@"

" withString:@""] stringByReplacingOccurrencesOfString:@"
" 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:@"

Ergebnisse 1 bis "]; int resultsLabelEnd = [page rangeOfString:@"

" 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:@"
"]) + [@"\n" length]; NSRange restRange = NSMakeRange(lyricsStart, [page length]-lyricsStart); int lyricsEnd = [page rangeOfString:@"
" options:NSCaseInsensitiveSearch range:restRange].location; NSString *lyrics = [self stringByRemovingHTMLTags:[page substringWithRange:NSMakeRange(lyricsStart, lyricsEnd-lyricsStart)]]; return lyrics; } @end