読者です 読者をやめる 読者になる 読者になる

エンジニアリングにはほど遠い

iPhoneアプリとかサイトとかをつくっていくブログです。

rtmpでニコ生タイムシフト保存の、iOSシミュレータ用のスクリプト(?)書いた

完全に自分用という事で。

なんでiOS用かというと、NSRegularExpressionがiOS用でしか使えなくて、でもこれを使わないと正規表現複数マッチのやり方が面倒そうだったのでもうこれでいいや、となりました。

Command Line Toolで作りたかったんですが、正規表現の使い方がよくわからず・・RegexKitを入れようにもlibicucoreが入んないんじゃないかとか思って・・ よくわかりませんでした(T_T)

では、初めてのシンタックスハイライトが見たいんでソースを載せてみます。

New ProjectのSingle View Applicationで作りました。

  • ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UITextFieldDelegate>{
    
    IBOutlet UITextField *field;
    IBOutlet UIButton *button;  
}
-(IBAction)generateSh:(id)sender;

@end
  • ViewController.m

#define LOGIN_ID @"****@******" //自分のID
#define PASSWORD @"*********" //自分のpasssord
して、以下のメソッド追加

-(IBAction)generateSh:(id)sender{
    
    if (!field.text) {
        NSLog(@"No lv");
        return;
    }
    NSString *lv = field.text;
    
    NSString *loginId = LOGIN_ID;
    NSString *passWord = PASSWORD;
    
    // Niconico login
    NSString *contentLogin = [NSString stringWithFormat:@"mail=%@&password=%@",loginId,passWord];
    NSURL *urlLogin = [NSURL URLWithString:@"https://secure.nicovideo.jp/secure/login?site=nicolive"];
    
    NSMutableURLRequest *urlRequestLogin = [[NSMutableURLRequest alloc]initWithURL:urlLogin];
    
    [urlRequestLogin setHTTPMethod:@"POST"];
    [urlRequestLogin setHTTPBody:[contentLogin dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSURLResponse* responseLogin;
    NSError* errorLogin = nil;
    NSData* resultLogin = [NSURLConnection sendSynchronousRequest:urlRequestLogin
                                                returningResponse:&responseLogin
                                                            error:&errorLogin];
    
    //NSLog(@"Data : %@", resultLogin);
    
    // Get API Data
    NSString *urlString = [NSString stringWithFormat:@"http://watch.live.nicovideo.jp/api/getplayerstatus?v=%@", lv];
    NSURL *url = [NSURL URLWithString:urlString];
    NSError *error = nil;
    NSString *string = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    NSLog(@"%@", string);
    
    // -r, -C のオプションパラメータ"URL", "TICKET"抽出
    // -rのと-Cのとで数が違う可能性があるので分けて考えた。いいやり方じゃ無さそう・・
    NSMutableArray *URL = [NSMutableArray array];
    NSString *TICKET;
    
    NSError *errorR   = nil;
    NSError *errorC   = nil;
    NSRegularExpression *regexpMinusR =
    [NSRegularExpression regularExpressionWithPattern:@"(rtmp://.*?),/(content.*?)</que>"
                                              options:0
                                                error:&errorR];
    NSRegularExpression *regexpMinusC =
    [NSRegularExpression regularExpressionWithPattern:@"<ticket>(.*)</ticket>"
                                              options:0
                                                error:&errorC];
    //"URL"生成(2個以上の場合アリ)
    int movieFileCount;
    if (errorR != nil) {
        NSLog(@"%@", errorR);
        return;
    } else {
        NSArray *matchR = [regexpMinusR matchesInString:string options:0 range:NSMakeRange(0, string.length)];
        NSLog(@"matchR count : %d", [matchR count]); //"URL"の数。つまり動画の数(途中で切れていると2個以上に)
        movieFileCount = [matchR count];
        
        
        for (int i = 0; i < [matchR count]; i++) {
            NSLog(@"matchR[%d:1]%@", i, [string substringWithRange:[[matchR objectAtIndex:i] rangeAtIndex:1]]); // (rtmp://.*?)部分
            NSLog(@"matchR[%d:2]%@", i, [string substringWithRange:[[matchR objectAtIndex:i] rangeAtIndex:2]]); // (content.*?)部分
            NSString *dockingStr = [NSString stringWithFormat:@"%@/mp4:%@", [string substringWithRange:[[matchR objectAtIndex:i] rangeAtIndex:1]], [string substringWithRange:[[matchR objectAtIndex:i] rangeAtIndex:2]]];
            //ここで合体して作っている。",/" → "/mp4:"のため
            [URL addObject:dockingStr];
        }
    }
    
    //"TICKET"生成
    if (errorC != nil) {
        NSLog(@"%@", errorC);
        return;
    } else {
        NSTextCheckingResult *matchC = [regexpMinusC firstMatchInString:string options:0 range:NSMakeRange(0, string.length)];
        NSLog(@"%d", matchC.numberOfRanges); // 
        NSLog(@"%@", [string substringWithRange:[matchC rangeAtIndex:1]]); // "TICKET"の中身
        TICKET = [string substringWithRange:[matchC rangeAtIndex:1]];
    }
    
    //スクリプトをファイル書き出しする
    NSMutableString *scriptText = [NSMutableString stringWithString:@"#!/bin/sh\n"];
    
    for (int i = 0; i < movieFileCount; i++) { //"URL"の数だけコマンド書き込み
        NSString *script;
        if (i == 0) {
            script = [NSString stringWithFormat:@"rtmpdump -r \"%@\" -C S:\"%@\" -f \"MAC 10,0,32,18\" -s \"http://live.nicovideo.jp/liveplayer.swf?20100531\" -o %@.flv\n", [URL objectAtIndex:i], TICKET, lv];
        }else{
            script = [NSString stringWithFormat:@"rtmpdump -r \"%@\" -C S:\"%@\" -f \"MAC 10,0,32,18\" -s \"http://live.nicovideo.jp/liveplayer.swf?20100531\" -o %@_%d.flv\n", [URL objectAtIndex:i], TICKET, lv, i+1];
        }
        [scriptText appendString:script];
    }    
    
    NSString *path = [NSString stringWithFormat:@"/Users/*****/%@.sh", lv];
    NSError* errorFile;
    BOOL result;
    //atomicallyは補助ファイルを使ってファイルの破損を防止するかどうか
    result = [scriptText writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&errorFile];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField{
	[textField resignFirstResponder];
	return YES;
}

あとはstoryboardでViewにUITextFieldとUIButtonを置いて各々リンクして完成です。
シミュレータで走らせて、テキストボックスにlv*******入れてボタンを押すと.shファイル生成。
生成された.shファイルを

chmod u+x lv*********.sh
./lv********.sh

で無事走ってくれました。(最後の./は環境変数次第でいらないらしいです)
動画が切れている時はちゃんと個数分生成してくれました。

まあ、なんかど素人丸出しのやり方だと思うんですが、シェルスクリプト的なものとか接続してAPIからパラメータを取ってくるとか初めてだったので勉強になりました。ニコ生リスナーの方々ありがとうございました。

あとはCookieを使った接続、動画にコメントが無いのでコメントの取得などもやりたいです。今の所全然分からないですけど。