Commit 51ace9075837caa4cf0230fb1b8eec9bf90fd776
Merge branch 'master' of bj.gitlab.cnlive.com:ios-team/LiveVideoCloud
Conflicts: LiveVideoCloud/Classes/Sections/LiveSections/Controller/LVCLiveViewController.m
Showing
9 changed files
with
122 additions
and
37 deletions
LiveVideoCloud/Classes/Sections/LiveSections/Controller/LVCLiveViewController.m
| ... | ... | @@ -37,7 +37,7 @@ |
| 37 | 37 | |
| 38 | 38 | @property (nonatomic, strong) UIButton *openFlashButton; |
| 39 | 39 | |
| 40 | -@property (nonatomic ,strong) LiveBaseView * _Nullable liveBaseView; | |
| 40 | +@property (nonatomic ,strong) LiveBaseView * _Nullable liveBaseView; | |
| 41 | 41 | |
| 42 | 42 | @property (nonatomic, strong) LVCUGCLiveEndView *endView; |
| 43 | 43 | |
| ... | ... | @@ -50,18 +50,20 @@ |
| 50 | 50 | |
| 51 | 51 | self.navigationController.delegate = self; |
| 52 | 52 | self.view.backgroundColor = [UIColor whiteColor]; |
| 53 | - [self.view addSubview:self.quitButton]; | |
| 54 | 53 | [self verifyLiveAuthority]; |
| 55 | 54 | } |
| 56 | 55 | |
| 57 | 56 | #pragma mark - 验证是否有主播权限 |
| 58 | 57 | - (void)verifyLiveAuthority { |
| 59 | 58 | |
| 59 | + [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeClear]; | |
| 60 | + [SVProgressHUD showWithStatus:@"正在加载..."]; | |
| 60 | 61 | [LVCVerifyLiveAuthService verifyLiveAuthWithHandler:^(id data, NSError *error) { |
| 61 | 62 | |
| 63 | + [SVProgressHUD dismiss]; | |
| 62 | 64 | if (error) { |
| 63 | 65 | NSLog(@"失败===>error:%@",error); |
| 64 | - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithFormat:@"抱歉, 鉴权失败~ "] delegate:nil cancelButtonTitle:@"好的" otherButtonTitles: nil]; | |
| 66 | + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithFormat:@"抱歉, 鉴权失败~ "] delegate:self cancelButtonTitle:@"好的" otherButtonTitles: nil]; | |
| 65 | 67 | alert.tag = 1005; |
| 66 | 68 | [alert show]; |
| 67 | 69 | |
| ... | ... | @@ -87,12 +89,14 @@ |
| 87 | 89 | //开始推流 |
| 88 | 90 | [self.view addSubview:self.captureView]; |
| 89 | 91 | [self.view addSubview:self.prepareView]; |
| 92 | + [self.view addSubview:self.quitButton]; | |
| 90 | 93 | [self.view addSubview:self.changeCameraButton]; |
| 91 | 94 | [self.view addSubview:self.openFilterButton]; |
| 92 | 95 | [self.view addSubview:self.openFlashButton]; |
| 93 | 96 | [self.view addSubview:self.filterView]; |
| 94 | 97 | |
| 95 | 98 | // 初始化推流 |
| 99 | + // 生成随机活动号 | |
| 96 | 100 | NSInteger time = [[NSDate date] timeIntervalSince1970]; |
| 97 | 101 | NSString *timeString = [NSString stringWithFormat:@"%ld", (long)time]; |
| 98 | 102 | NSString *random = [NSString stringWithFormat:@"%d%d%d%d%d",arc4random()%9,arc4random()%9,arc4random()%9,arc4random()%9,arc4random()%9]; |
| ... | ... | @@ -102,12 +106,16 @@ |
| 102 | 106 | |
| 103 | 107 | _kitCN = [[CNLiveGPUStreamerKit alloc] initCNLiveKitWithInterruptConfigWithPureAudioMode:NO]; |
| 104 | 108 | _kitCN.activityId = activityId; |
| 105 | - _kitCN.channelId = @"iOStest"; | |
| 109 | + _kitCN.channelId = data[@"channelId"]; | |
| 106 | 110 | |
| 111 | + // 主播ID | |
| 112 | + NSString *channelName = data[@"channelName"]; | |
| 113 | + NSString *coverImgUrl = data[@"coverImgUrl"]; | |
| 114 | + | |
| 107 | 115 | // 封面图 |
| 108 | - _kitCN.coverImgUrl = @"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=169300069,2234409601&fm=21&gp=0.jpg"; | |
| 116 | + _kitCN.coverImgUrl = coverImgUrl; | |
| 109 | 117 | // 昵称 头像 |
| 110 | - NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:@"我的名字好长啊", @"name", @"http://www.qq745.com/uploads/allimg/150318/1-15031PGF8-51.jpg", @"iconUrl", nil]; | |
| 118 | + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:channelName, @"name", [UserInformationModel manager].faceUrl, @"iconUrl", nil]; | |
| 111 | 119 | _kitCN.extensions = [self dictionaryToJson:userInfo]; |
| 112 | 120 | |
| 113 | 121 | // 设置推流配置 |
| ... | ... | @@ -120,7 +128,7 @@ |
| 120 | 128 | [self.captureView addGestureRecognizer:singleRecognizer]; |
| 121 | 129 | |
| 122 | 130 | } else { //禁用 |
| 123 | - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"抱歉, 您没有权限~" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles: nil]; | |
| 131 | + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"抱歉, 您没有权限~" delegate:self cancelButtonTitle:@"好的" otherButtonTitles: nil]; | |
| 124 | 132 | alert.tag = 1004; |
| 125 | 133 | [alert show]; |
| 126 | 134 | } |
| ... | ... | @@ -340,22 +348,23 @@ |
| 340 | 348 | |
| 341 | 349 | if ( _kitCN.captureState == CNLiveCaptureStateIdle){ |
| 342 | 350 | //初始化时状态为空闲 |
| 343 | - | |
| 351 | + NSLog(@"---------CNLiveCaptureStateIdle"); | |
| 344 | 352 | return; |
| 345 | 353 | } |
| 346 | 354 | else if (_kitCN.captureState == CNLiveCaptureStateCapturing ) { |
| 347 | 355 | //设备工作中 |
| 348 | - | |
| 356 | + NSLog(@"---------CNLiveCaptureStateCapturing"); | |
| 349 | 357 | return; |
| 350 | 358 | } |
| 351 | 359 | else if (_kitCN.captureState == CNLiveCaptureStateClosingCapture ) { |
| 352 | 360 | //关闭采集设备中 |
| 353 | - | |
| 361 | + NSLog(@"---------CNLiveCaptureStateClosingCapture"); | |
| 354 | 362 | return; |
| 355 | 363 | } |
| 356 | 364 | else if (_kitCN.captureState == CNLiveCaptureStateDevAuthDenied ) { |
| 357 | 365 | //设备授权被拒绝 |
| 358 | 366 | NSLog(@"---------CNLiveCaptureStateDevAuthDenied"); |
| 367 | + [self toast:@"设备未授权,请授权~"]; | |
| 359 | 368 | |
| 360 | 369 | return; |
| 361 | 370 | } |
| ... | ... | @@ -379,24 +388,17 @@ |
| 379 | 388 | NSLog(@"---------CNLiveStreamStateIdle"); |
| 380 | 389 | } |
| 381 | 390 | |
| 382 | - if (_kitCN.streamState == CNLiveStreamStateConnected) { | |
| 383 | - | |
| 384 | - NSLog(@"---------CNLiveStreamStateConnected"); | |
| 385 | - } | |
| 386 | - | |
| 387 | 391 | if (_kitCN.streamState == CNLiveStreamStateConnecting) { |
| 388 | 392 | |
| 389 | 393 | NSLog(@"---------CNLiveStreamStateConnecting"); |
| 390 | 394 | } |
| 391 | 395 | |
| 392 | - //推流出错 | |
| 393 | - if (_kitCN.streamState == CNLiveStreamStateError) { | |
| 396 | + if (_kitCN.streamState == CNLiveStreamStateConnected) { | |
| 394 | 397 | |
| 395 | - NSLog(@"---------CNLiveStreamStateError"); | |
| 396 | - [self onStreamError]; | |
| 398 | + NSLog(@"---------CNLiveStreamStateConnected"); | |
| 397 | 399 | } |
| 398 | 400 | |
| 399 | - //断开推流 | |
| 401 | + //断开推流中 | |
| 400 | 402 | if ( _kitCN.streamState == CNLiveStreamStateDisconnecting) { |
| 401 | 403 | |
| 402 | 404 | NSLog(@"---------CNLiveStreamStateDisconnecting"); |
| ... | ... | @@ -407,6 +409,20 @@ |
| 407 | 409 | |
| 408 | 410 | NSLog(@"---------CNLiveStopStreamSuccess"); |
| 409 | 411 | } |
| 412 | + | |
| 413 | + //结束推流失败 | |
| 414 | + if ( _kitCN.streamState == CNLiveStopStreamFailure) { | |
| 415 | + | |
| 416 | + NSLog(@"---------CNLiveStopStreamFailure"); | |
| 417 | + } | |
| 418 | + | |
| 419 | + //推流出错 | |
| 420 | + if (_kitCN.streamState == CNLiveStreamStateError) { | |
| 421 | + | |
| 422 | + NSLog(@"---------CNLiveStreamStateError"); | |
| 423 | + [self onStreamError]; | |
| 424 | + } | |
| 425 | + | |
| 410 | 426 | } |
| 411 | 427 | |
| 412 | 428 | - (void)onStreamError { // 推流失败 |
| ... | ... | @@ -430,19 +446,15 @@ |
| 430 | 446 | else if ( CNLiveStreamErrorCode_ABNORMAL == err) { |
| 431 | 447 | |
| 432 | 448 | [self toast:@"推流异常中断~~"]; |
| 449 | + [self tryReconnect]; | |
| 433 | 450 | |
| 434 | 451 | } else if (err == CNLiveStreamErrorCode_CODEC_OPEN_FAILED) { |
| 435 | 452 | NSLog(@"video codec open failed, try software codec"); |
| 436 | 453 | // 换个编码类型重新尝试 |
| 437 | 454 | _kitCN.streamerBase.videoCodec = CNLiveVideoCodec_VT264; |
| 438 | 455 | [self tryReconnect]; |
| 439 | - } | |
| 440 | - else { | |
| 441 | 456 | |
| 442 | - } | |
| 443 | - | |
| 444 | - // 断网重连 | |
| 445 | - if (err == CNLiveStreamErrorCode_CONNECT_BREAK || err == CNLiveStreamErrorCode_AV_SYNC_ERROR) { | |
| 457 | + } else if (err == CNLiveStreamErrorCode_CONNECT_BREAK || err == CNLiveStreamErrorCode_AV_SYNC_ERROR) { | |
| 446 | 458 | [self tryReconnect]; |
| 447 | 459 | } |
| 448 | 460 | |
| ... | ... | @@ -600,6 +612,7 @@ |
| 600 | 612 | } |
| 601 | 613 | |
| 602 | 614 | #pragma mark - Action !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| 615 | + | |
| 603 | 616 | - (void)addObservers { |
| 604 | 617 | |
| 605 | 618 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureStateChange) name:CNLiveCaptureStateDidChangeNotification object:nil]; | ... | ... |
LiveVideoCloud/Classes/Sections/LiveSections/Controller/LVCVerifyViewController.m
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | |
| 9 | 9 | #import "LVCVerifyViewController.h" |
| 10 | 10 | #import <WebKit/WebKit.h> |
| 11 | +#import "LVCLiveViewController.h" | |
| 11 | 12 | |
| 12 | 13 | @interface LVCVerifyViewController ()<WKUIDelegate, WKNavigationDelegate> |
| 13 | 14 | |
| ... | ... | @@ -20,6 +21,18 @@ |
| 20 | 21 | |
| 21 | 22 | self.view.backgroundColor = [UIColor blueColor]; |
| 22 | 23 | |
| 24 | + self.navigationItem.hidesBackButton = YES; | |
| 25 | + UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 44, 44)]; | |
| 26 | + [button setImage:[[UIImage imageNamed:@"fanhui"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; | |
| 27 | + [button addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside]; | |
| 28 | + UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithCustomView:button]; | |
| 29 | + UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] | |
| 30 | + initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace | |
| 31 | + target:nil action:nil]; | |
| 32 | + negativeSpacer.width = -15; | |
| 33 | + self.navigationItem.leftBarButtonItems = @[negativeSpacer, backItem]; | |
| 34 | + | |
| 35 | + | |
| 23 | 36 | WKWebView *webView = [[WKWebView alloc]init]; |
| 24 | 37 | [self.view addSubview:webView]; |
| 25 | 38 | [webView mas_makeConstraints:^(MASConstraintMaker *make) { |
| ... | ... | @@ -32,10 +45,17 @@ |
| 32 | 45 | |
| 33 | 46 | } |
| 34 | 47 | |
| 48 | +- (void)back { | |
| 49 | + | |
| 50 | + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; | |
| 51 | +} | |
| 52 | + | |
| 35 | 53 | #pragma mark - WKNavigationDelegate |
| 36 | 54 | // 页面开始加载时调用 |
| 37 | 55 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{ |
| 38 | 56 | |
| 57 | + [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeClear]; | |
| 58 | + [SVProgressHUD showWithStatus:@"正在加载..."]; | |
| 39 | 59 | } |
| 40 | 60 | // 当内容开始返回时调用 |
| 41 | 61 | - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{ |
| ... | ... | @@ -44,12 +64,15 @@ |
| 44 | 64 | // 页面加载完成之后调用 |
| 45 | 65 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{ |
| 46 | 66 | |
| 67 | + [SVProgressHUD dismiss]; | |
| 47 | 68 | } |
| 48 | 69 | // 页面加载失败时调用 |
| 49 | -- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{ | |
| 70 | +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { | |
| 50 | 71 | |
| 51 | - [Tools showHUDInView:self.view withString:@"加载失败,请稍后重试..." autoHide:YES]; | |
| 72 | + [SVProgressHUD dismiss]; | |
| 73 | + [self toast:@"加载失败,请稍后重试..."]; | |
| 52 | 74 | } |
| 75 | + | |
| 53 | 76 | // 接收到服务器跳转请求之后调用 |
| 54 | 77 | - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{ |
| 55 | 78 | |
| ... | ... | @@ -91,5 +114,19 @@ |
| 91 | 114 | completionHandler(); |
| 92 | 115 | } |
| 93 | 116 | |
| 117 | +- (void)toast:(NSString*)message { // 提示框 | |
| 118 | + | |
| 119 | + UIAlertView *toast = [[UIAlertView alloc] initWithTitle:nil | |
| 120 | + message:message | |
| 121 | + delegate:nil | |
| 122 | + cancelButtonTitle:nil | |
| 123 | + otherButtonTitles:nil, nil]; | |
| 124 | + [toast show]; | |
| 125 | + double duration = 2.5; | |
| 126 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 127 | + [toast dismissWithClickedButtonIndex:0 animated:NO]; | |
| 128 | + }); | |
| 129 | +} | |
| 130 | + | |
| 94 | 131 | |
| 95 | 132 | @end | ... | ... |
LiveVideoCloud/Classes/Sections/LiveSections/View/LVCUGCLiveEndView.m
| ... | ... | @@ -39,8 +39,10 @@ |
| 39 | 39 | - (UIImageView *)iconView { |
| 40 | 40 | |
| 41 | 41 | if (!_iconView) { |
| 42 | - NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 43 | - UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 42 | +// NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 43 | +// UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 44 | + UIImage *image = [UIImage imageWithData:[NSData | |
| 45 | + dataWithContentsOfURL:[NSURL URLWithString:[UserInformationModel manager].faceUrl]]]; | |
| 44 | 46 | _iconView = [[UIImageView alloc] initWithImage:image]; |
| 45 | 47 | _iconView.frame = CGRectMake((selfLength - 50) * 0.5, 20, 50, 50); |
| 46 | 48 | _iconView.layer.cornerRadius = 25; | ... | ... |
LiveVideoCloud/Classes/Sections/LiveSections/View/LVCUGCLivePrepareView.m
| ... | ... | @@ -35,8 +35,11 @@ |
| 35 | 35 | - (UIImageView *)iconView { |
| 36 | 36 | |
| 37 | 37 | if (!_iconView) { |
| 38 | - NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 39 | - UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 38 | +// NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 39 | +// UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 40 | + | |
| 41 | + UIImage *image = [UIImage imageWithData:[NSData | |
| 42 | + dataWithContentsOfURL:[NSURL URLWithString:[UserInformationModel manager].faceUrl]]]; | |
| 40 | 43 | _iconView = [[UIImageView alloc] initWithImage:image]; |
| 41 | 44 | _iconView.frame = CGRectMake(10, SCREEN_HEIGHT * 0.35, 90, 90); |
| 42 | 45 | _iconView.layer.cornerRadius = 45; | ... | ... |
LiveVideoCloud/Classes/Sections/homeSections/Controller/LVCUGCLiveListView.m
| ... | ... | @@ -62,7 +62,7 @@ static NSString *cellId = @"cellId"; |
| 62 | 62 | } |
| 63 | 63 | return ; |
| 64 | 64 | } |
| 65 | - NSLog(@"成功===>data%@",data); | |
| 65 | +// NSLog(@"成功===>data%@",data); | |
| 66 | 66 | |
| 67 | 67 | LVCUGCLiveListModel *model = [LVCUGCLiveListModel modelWithDic:data]; |
| 68 | 68 | _arrayM = model.activityList.mutableCopy; |
| ... | ... | @@ -91,6 +91,9 @@ static NSString *cellId = @"cellId"; |
| 91 | 91 | LVCUGCLivePlayerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath]; |
| 92 | 92 | |
| 93 | 93 | LVCUGCLivePlayerModel *model = _arrayM[indexPath.row]; |
| 94 | + if (indexPath.row == 0) { | |
| 95 | + NSLog(@"%@",model.extensionName); | |
| 96 | + } | |
| 94 | 97 | cell.model = model; |
| 95 | 98 | |
| 96 | 99 | return cell; | ... | ... |
LiveVideoCloud/Classes/Sections/homeSections/View/LVCUGCBackgroundView.m
| ... | ... | @@ -77,8 +77,10 @@ |
| 77 | 77 | - (UIImageView *)iconView { |
| 78 | 78 | |
| 79 | 79 | if (!_iconView) { |
| 80 | - NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 81 | - UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 80 | +// NSString *path = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 81 | +// UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
| 82 | + UIImage *image = [UIImage imageWithData:[NSData | |
| 83 | + dataWithContentsOfURL:[NSURL URLWithString:[UserInformationModel manager].faceUrl]]]; | |
| 82 | 84 | _iconView = [[UIImageView alloc] initWithImage:image]; |
| 83 | 85 | _iconView.frame = CGRectMake((SCREEN_WIDTH - 100) * 0.5, SCREEN_HEIGHT * 0.2, 100, 100); |
| 84 | 86 | _iconView.layer.cornerRadius = 50; | ... | ... |
LiveVideoCloud/Classes/Sections/homeSections/View/LVCUGCLivePlayerCell.m
| ... | ... | @@ -63,9 +63,13 @@ |
| 63 | 63 | |
| 64 | 64 | if (model.extensionName.length > 0) { |
| 65 | 65 | _nameLabel.text = model.extensionName; |
| 66 | + } else { | |
| 67 | + _nameLabel.text = @"我的昵称"; | |
| 66 | 68 | } |
| 67 | - NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 68 | - UIImage *iconImage = [UIImage imageWithContentsOfFile:iconPath]; | |
| 69 | +// NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"头像" ofType:@"jpg"]; | |
| 70 | +// UIImage *iconImage = [UIImage imageWithContentsOfFile:iconPath]; | |
| 71 | + UIImage *iconImage = [UIImage imageWithData:[NSData | |
| 72 | + dataWithContentsOfURL:[NSURL URLWithString:[UserInformationModel manager].faceUrl]]]; | |
| 69 | 73 | [_iconView sd_setImageWithURL:[NSURL URLWithString:model.extensionIcon] placeholderImage:iconImage]; |
| 70 | 74 | |
| 71 | 75 | switch ([model.activityStatus intValue]) { | ... | ... |
LiveVideoCloud/animation.xcassets/fanhui.imageset/Contents.json
0 → 100644
| 1 | +{ | |
| 2 | + "images" : [ | |
| 3 | + { | |
| 4 | + "idiom" : "universal", | |
| 5 | + "scale" : "1x" | |
| 6 | + }, | |
| 7 | + { | |
| 8 | + "idiom" : "universal", | |
| 9 | + "filename" : "fanhui.png", | |
| 10 | + "scale" : "2x" | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "idiom" : "universal", | |
| 14 | + "scale" : "3x" | |
| 15 | + } | |
| 16 | + ], | |
| 17 | + "info" : { | |
| 18 | + "version" : 1, | |
| 19 | + "author" : "xcode" | |
| 20 | + } | |
| 21 | +} | |
| 0 | 22 | \ No newline at end of file | ... | ... |
LiveVideoCloud/animation.xcassets/fanhui.imageset/fanhui.png
0 → 100644
1.57 KB