MLTitleDecrapifier.m 8.67 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*****************************************************************************
 * MLTitleDecrapifier.m
 * Lunettes
 *****************************************************************************
 * Copyright (C) 2010 Pierre d'Herbemont
 * Copyright (C) 2010-2013 VLC authors and VideoLAN
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
10
 *          Felix Paul Kühne <fkuehne # videolan.org>
11
 *
12 13 14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15 16 17 18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
22 23 24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
 *****************************************************************************/
Pierre's avatar
Pierre committed
26 27 28 29 30 31 32

#import "MLTitleDecrapifier.h"

@implementation MLTitleDecrapifier
+ (NSString *)decrapify:(NSString *)string
{
    static NSArray *ignoredWords = nil;
33
    if (!ignoredWords)
Pierre's avatar
Pierre committed
34
        ignoredWords = [[NSArray alloc] initWithObjects:
35
                        @"xvid", @"h264", @"dvd", @"rip", @"divx", @"[fr]", @"720p", @"1080i", @"1080p", @"x264", @"hdtv", @"aac", @"bluray", nil];
36

Pierre's avatar
Pierre committed
37 38 39 40
    NSMutableString *mutableString = [NSMutableString stringWithString:string];
    for (NSString *word in ignoredWords)
        [mutableString replaceOccurrencesOfString:word withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [mutableString length])];
    [mutableString replaceOccurrencesOfString:@"." withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, [mutableString length])];
41
    [mutableString replaceOccurrencesOfString:@"_" withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, [mutableString length])];
42
    [mutableString replaceOccurrencesOfString:@"+" withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, [mutableString length])];
43 44 45 46 47 48 49 50 51
    [mutableString replaceOccurrencesOfString:@"-" withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, [mutableString length])];

    NSString *staticString = [NSString stringWithString:mutableString];
    mutableString = nil;

    while ([staticString rangeOfString:@"  "].location != NSNotFound)
        staticString = [staticString stringByReplacingOccurrencesOfString:@"  " withString:@" "];

    if (staticString.length > 2) {
52 53 54 55 56 57
        @try {
            if ([staticString characterAtIndex:0] == 0x20)
                staticString = [staticString substringFromIndex:1];
        }
        @catch (NSException *exception) {
        }
58 59 60
    }

    return staticString;
Pierre's avatar
Pierre committed
61 62 63 64 65 66 67
}

static inline BOOL isDigit(char c)
{
    return c >= '0' && c <= '9';
}

68 69 70
// Shortcut to ease reading
static inline unichar c(NSString *string, unsigned index)
{
71 72 73 74 75 76
    @try {
        return [string characterAtIndex:index];
    }
    @catch (NSException *exception) {
        return 0x00;
    }
77 78 79
}


Pierre's avatar
Pierre committed
80 81
+ (BOOL)isTVShowEpisodeTitle:(NSString *)string
{
82
    NSString *str = [string lowercaseString];
Pierre's avatar
Pierre committed
83 84

    // Search for s01e10.
85 86 87 88 89 90 91
    for (int i = 0; i < (int)[str length] - 5; i++) {
        if (c(str, i) == 's' &&
            isDigit(c(str, i+1)) &&
            isDigit(c(str, i+2)) &&
            c(str, i+3) == 'e' &&
            isDigit(c(str, i+4)) &&
            isDigit(c(str, i+5)))
Pierre's avatar
Pierre committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105
        {
            return YES;
        }
    }
    return NO;
}

static inline int intFromChar(char n)
{
    return n - '0';
}

static inline NSNumber *numberFromTwoChars(char high, char low)
{
106
    return @(intFromChar(high) * 10 + intFromChar(low));
Pierre's avatar
Pierre committed
107 108
}

109 110 111 112 113
static inline NSNumber *numberFromThreeChars(char high, char mid, char low)
{
    return @(intFromChar(high) * 100 + intFromChar(mid) * 10 + intFromChar(low));
}

Pierre's avatar
Pierre committed
114 115
+ (NSDictionary *)tvShowEpisodeInfoFromString:(NSString *)string
{
116 117
    if (!string)
        return nil;
118
    NSString *str = [string lowercaseString];
119 120 121
    BOOL successfulSearch = NO;
    NSMutableDictionary *mutableDict;
    NSUInteger stringLength = [str length];
Pierre's avatar
Pierre committed
122

123 124 125
    if (stringLength < 6)
        return nil;

Pierre's avatar
Pierre committed
126
    // Search for s01e10.
127
    for (NSUInteger i = 0; i < stringLength - 5; i++) {
128 129 130 131 132 133
        if (c(str, i) == 's' &&
            isDigit(c(str, i+1)) &&
            isDigit(c(str, i+2)) &&
            c(str, i+3) == 'e' &&
            isDigit(c(str, i+4)) &&
            isDigit(c(str, i+5)))
Pierre's avatar
Pierre committed
134
        {
135
            NSNumber *season = numberFromTwoChars(c(str,i+1), c(str,i+2));
136 137 138 139 140
            NSNumber *episode;
            if (isDigit(c(str, i+6)))
                episode = numberFromThreeChars(c(str,i+4), c(str,i+5), c(str,i+6));
            else
                episode = numberFromTwoChars(c(str,i+4), c(str,i+5));
141
            NSString *tvShowName = i > 0 ? [str substringToIndex:i-1] : nil;
Pierre's avatar
Pierre committed
142
            tvShowName = tvShowName ? [[MLTitleDecrapifier decrapify:tvShowName] capitalizedString] : nil;
143
            NSString *episodeName = stringLength > i + 4 ? [str substringFromIndex:i+6] : nil;
144 145 146 147 148 149 150 151 152 153

            NSArray *components = [episodeName componentsSeparatedByString:@" "];
            NSUInteger componentsCount = components.count;

            episodeName = episodeName ? [MLTitleDecrapifier decrapify:episodeName] : nil;

            /* episode name is optional */
            if ([episodeName isEqualToString:components[componentsCount - 1]])
                episodeName = nil;

154
            mutableDict = [[NSMutableDictionary alloc] initWithCapacity:4];
155
            if (season)
156
                [mutableDict setObject:season forKey:@"season"];
157
            if (episode)
158
                [mutableDict setObject:episode forKey:@"episode"];
159
            if (tvShowName && ![tvShowName isEqualToString:@" "])
160
                [mutableDict setObject:tvShowName forKey:@"tvShowName"];
161
            if (episodeName.length > 0 && ![episodeName isEqualToString:@" "])
162 163
                [mutableDict setObject:[episodeName capitalizedString] forKey:@"tvEpisodeName"];
            successfulSearch = YES;
Pierre's avatar
Pierre committed
164 165 166
        }
    }

167 168 169 170 171 172 173 174 175 176 177 178 179
    // search for 0x00
    if (!successfulSearch) {
        for (NSUInteger i = 0; i < stringLength - 4; i++) {
            if (isDigit(c(str, i)) &&
                c(str, i+1) == 'x' &&
                isDigit(c(str, i+2)) &&
                isDigit(c(str, i+3)))
            {
                NSNumber *season = [NSNumber numberWithInt:intFromChar(c(str,i))];
                NSNumber *episode = numberFromTwoChars(c(str,i+2), c(str,i+3));
                NSString *tvShowName = i > 0 ? [str substringToIndex:i-1] : nil;
                tvShowName = tvShowName ? [[MLTitleDecrapifier decrapify:tvShowName] capitalizedString] : nil;

180 181 182 183 184 185 186 187 188 189 190
                NSString *episodeName = stringLength > i + 4 ? [str substringFromIndex:i+4] : nil;

                NSArray *components = [episodeName componentsSeparatedByString:@" "];
                NSUInteger componentsCount = components.count;

                episodeName = episodeName ? [MLTitleDecrapifier decrapify:episodeName] : nil;

                /* episode name is optional */
                if ([episodeName isEqualToString:components[componentsCount - 1]])
                    episodeName = nil;

191 192 193 194 195 196 197
                mutableDict = [[NSMutableDictionary alloc] initWithCapacity:3];
                if (season)
                    [mutableDict setObject:season forKey:@"season"];
                if (episode)
                    [mutableDict setObject:episode forKey:@"episode"];
                if (tvShowName && ![tvShowName isEqualToString:@" "])
                    [mutableDict setObject:tvShowName forKey:@"tvShowName"];
198 199
                if (episodeName.length > 0 && ![episodeName isEqualToString:@" "])
                    [mutableDict setObject:[episodeName capitalizedString] forKey:@"tvEpisodeName"];
200 201 202 203 204
                successfulSearch = YES;
            }
        }
    }

205 206 207 208 209
    if (successfulSearch) {
        NSDictionary *dict = [NSDictionary dictionaryWithDictionary:mutableDict];
        [mutableDict release];
        return dict;
    }
210 211

    return nil;
Pierre's avatar
Pierre committed
212
}
213 214 215 216 217 218

+ (NSDictionary *)audioContentInfoFromFile:(MLFile *)file
{
    if (!file)
        return nil;

219 220 221 222 223 224 225 226 227
    NSString *title = file.title;
    NSArray *components = [title componentsSeparatedByString:@" "];
    if (components.count > 0) {
        if ([components[0] intValue] > 0)
            title = [self decrapify:[title stringByReplacingOccurrencesOfString:components[0] withString:@""]];
    } else
        title = [self decrapify:title];

    return [NSDictionary dictionaryWithObject:title forKey:VLCMetaInformationTitle];
228 229
}

Pierre's avatar
Pierre committed
230
@end