Skip to content
This repository has been archived by the owner on Oct 9, 2023. It is now read-only.

Commit

Permalink
Add can download video, release v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
haoict committed May 26, 2020
1 parent f5f05b7 commit 0867fe6
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 17 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Free & Open Source Tweak for Twitter app on iOS!

## Features
- Remove Ads
- Can Save Video (long press on video to save)
- Lightweight
- Support iOS 10 (not tested) - 11 (tested) - 12 (tested) - 13 (tested)
- Support latest Twitter version (If it doesn't work, you should update the app to latest version)
Expand Down
44 changes: 44 additions & 0 deletions Tweak.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,52 @@
#import <Foundation/Foundation.h>
#import <libhdev/HUtilities/HDownloadMedia.h>

#define PLIST_PATH "/var/mobile/Library/Preferences/com.haoict.twitternoadspref.plist"
#define PREF_CHANGED_NOTIF "com.haoict.twitternoadspref/PrefChanged"

@interface TFNItemsDataViewController : NSObject
- (id)itemAtIndexPath:(id)arg1;
@end

@interface TFSTwitterEntityMediaVideoVariant : NSObject
@property(readonly, nonatomic) long long bitrate;
@property(readonly, copy, nonatomic) NSString *contentType;
@property(readonly, copy, nonatomic) NSString *url;
@end

@interface TFSTwitterEntityMediaVideoInfo : NSObject
@property(readonly, copy, nonatomic) NSArray *variants;
@end

@interface TFSTwitterEntityMedia : NSObject
@property(readonly, nonatomic) TFSTwitterEntityMediaVideoInfo *videoInfo;
@end

@interface TFSTwitterMediaInfo : NSObject
@property(retain, nonatomic) TFSTwitterEntityMedia *mediaEntity;
@end

@protocol T1StatusViewModel
@property(readonly, nonatomic) TFSTwitterMediaInfo *primaryMediaInfo;
@end

@interface TFNTwitterStatus : NSObject<T1StatusViewModel>
@property (nonatomic, readonly, strong) NSString *primaryMediaVideoURL;
@end

// @interface T1StatusInlineMediaViewModel : NSObject
// @property(readonly, nonatomic) id<T1StatusViewModel> viewModel;
// @end

// @interface T1InlineMediaContainerView : UIView
// @property (nonatomic, readonly, strong) UIViewController *tfn_viewController;
// @end

// @interface T1InlineMediaView : UIView
// @property(readonly, nonatomic) UILongPressGestureRecognizer *longPressGestureRecognizer;
// @property(retain, nonatomic) T1StatusInlineMediaViewModel *viewModel;
// @end

@interface T1SlideshowViewController : UIViewController
@property (nonatomic, readonly, strong) id<T1StatusViewModel> slideStatus;
@end
86 changes: 70 additions & 16 deletions Tweak.xm
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,95 @@
* Load Preferences
*/
BOOL noads;
BOOL canSaveVideo;

static void reloadPrefs() {
NSDictionary *settings = [[NSMutableDictionary alloc] initWithContentsOfFile:@PLIST_PATH] ?: [@{} mutableCopy];

noads = [[settings objectForKey:@"noads"] ?: @(YES) boolValue];
canSaveVideo = [[settings objectForKey:@"canSaveVideo"] ?: @(YES) boolValue];
}

static void showDownloadPopup(id twStatus, UIViewController *viewController, void (^origHandler)(UIAlertAction *)) {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? UIAlertControllerStyleAlert : UIAlertControllerStyleActionSheet];
[alert addAction:[UIAlertAction actionWithTitle:@"Save Video" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString* videoUrl = nil;
if ([twStatus isKindOfClass:%c(TFNTwitterStatus)]) {
videoUrl = ((TFNTwitterStatus *)twStatus).primaryMediaVideoURL;
}
// if primary video is m3u8 format, we can't save it, thus we have to look for another place to find .mp4 and save highest bitrate
if (!videoUrl || [videoUrl containsString:@".m3u8"]) {
long long bitrate = 0;
for (TFSTwitterEntityMediaVideoVariant *video in ((id<T1StatusViewModel>)twStatus).primaryMediaInfo.mediaEntity.videoInfo.variants) {
if ([video.contentType isEqualToString:@"video/mp4"] && video.bitrate > bitrate) {
bitrate = video.bitrate;
videoUrl = video.url;
}
}
}
[HDownloadMedia checkPermissionToPhotosAndDownload:videoUrl appendExtension:nil mediaType:Video toAlbum:@"Twitter"];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Twitter Options" style:UIAlertActionStyleDefault handler:origHandler]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[viewController presentViewController:alert animated:YES completion:nil];
}

%group NoAds
%hook TFNItemsDataViewController
- (id)tableViewCellForItem:(id)arg1 atIndexPath:(id)arg2 {
UITableViewCell *tbvCell = %orig;
id item = [self itemAtIndexPath: arg2];
if ([item respondsToSelector: @selector(isPromoted)] && [item performSelector:@selector(isPromoted)]) {
[tbvCell setHidden: YES];
- (id)tableViewCellForItem:(id)arg1 atIndexPath:(id)arg2 {
UITableViewCell *tbvCell = %orig;
id item = [self itemAtIndexPath: arg2];
if ([item respondsToSelector: @selector(isPromoted)] && [item performSelector:@selector(isPromoted)]) {
[tbvCell setHidden: YES];
}
return tbvCell;
}
return tbvCell;
}

- (double)tableView:(id)arg1 heightForRowAtIndexPath:(id)arg2 {
id item = [self itemAtIndexPath: arg2];
if ([item respondsToSelector: @selector(isPromoted)] && [item performSelector:@selector(isPromoted)]) {
return 0;
- (double)tableView:(id)arg1 heightForRowAtIndexPath:(id)arg2 {
id item = [self itemAtIndexPath: arg2];
if ([item respondsToSelector: @selector(isPromoted)] && [item performSelector:@selector(isPromoted)]) {
return 0;
}
return %orig;
}
%end
%end

%group SaveVideo
%hook T1SlideshowViewController
- (void)imageDisplayView:(id)arg1 didLongPress:(id)arg2 {
%orig;
}

- (void)slideshowSeekController:(id)arg1 didLongPressWithRecognizer:(id)arg2 {
id<T1StatusViewModel> twStatus = self.slideStatus;
showDownloadPopup(twStatus, self, ^(UIAlertAction *action) { %orig; });
}
return %orig;
}
%end

// doesn't work for ios 13 since it has haptic touch
// %hook T1InlineMediaView
// - (void)_t1_longPressAction {
// if (self.longPressGestureRecognizer.state == UIGestureRecognizerStateBegan) {
// UIViewController *vc = ((T1InlineMediaContainerView *)self.superview).tfn_viewController;
// id<T1StatusViewModel> twStatus = self.viewModel.viewModel;
// showDownloadPopup(twStatus, vc, ^(UIAlertAction *action) { %orig; });
// } else {
// %orig;
// }
// }
// %end
%end

%ctor {
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback) reloadPrefs, CFSTR(PREF_CHANGED_NOTIF), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
reloadPrefs();

if (!noads) {
return;
if (noads) {
%init(NoAds);
}

%init(NoAds);
if (canSaveVideo) {
%init(SaveVideo);
}
}
3 changes: 2 additions & 1 deletion control
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
Package: com.haoict.twitternoads
Name: Twitter No Ads
Depends: mobilesubstrate, preferenceloader, com.haoict.libhdev, firmware (>= 10)
Depends: mobilesubstrate, preferenceloader, com.haoict.libhdev (>= 2.0.0), firmware (>= 10)
Version: 1.0.0
Architecture: iphoneos-arm
Description: Free & Open Source Tweak for Twitter
Free & Open Source Tweak for Twitter
Features:
- Remove Ads
- Can Save Video (long press on video to save)
- Lightweight
- Support iOS 10 (not tested) - 11 (tested) - 12 (tested) - 13 (tested)
- Support latest Twitter version (If it doesn't work, you should update the app to latest version)
Expand Down
19 changes: 19 additions & 0 deletions pref/Resources/Root.plist
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@
<string>com.haoict.twitternoadspref/PrefChanged</string>
</dict>

<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>cellClass</key>
<string>HPSSwitchCell</string>
<key>default</key>
<true/>
<key>defaults</key>
<string>com.haoict.twitternoadspref</string>
<key>key</key>
<string>canSaveVideo</string>
<key>label</key>
<string>CAN_SAVE_VIDEO</string>
<key>subtitle</key>
<string>LONG_PRESS_ON_VIDEO_TO_SAVE</string>
<key>PostNotification</key>
<string>com.haoict.twitternoadspref/PrefChanged</string>
</dict>

<!-- Support Me -->
<dict>
<key>cell</key>
Expand Down
2 changes: 2 additions & 0 deletions pref/Resources/base.lproj/Root.strings
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"MAIN_PREFERENCES" = "Main Preferences";
"NO_ADS" = "No Ads";
"REMOVE_ADS_IN_NEWS_FEED" = "Remove Ads in News Feed";
"CAN_SAVE_VIDEO" = "Can Save Video";
"LONG_PRESS_ON_VIDEO_TO_SAVE" = "Long press on video to save";
"OTHER_PREFERENCES" = "Other Preferences";
"RESET_SETTINGS" = "Reset Settings";
"SUPPORT_ME" = "Support Me";
Expand Down
2 changes: 2 additions & 0 deletions pref/Resources/en.lproj/Root.strings
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"MAIN_PREFERENCES" = "Main Preferences";
"NO_ADS" = "No Ads";
"REMOVE_ADS_IN_NEWS_FEED" = "Remove Ads in News Feed";
"CAN_SAVE_VIDEO" = "Can Save Video";
"LONG_PRESS_ON_VIDEO_TO_SAVE" = "Long press on video to save";
"OTHER_PREFERENCES" = "Other Preferences";
"RESET_SETTINGS" = "Reset Settings";
"SUPPORT_ME" = "Support Me";
Expand Down
2 changes: 2 additions & 0 deletions pref/Resources/vi.lproj/Root.strings
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"DEFAULT" = "Mặc định";
"MAIN_PREFERENCES" = "Tùy chỉnh chính";
"NO_ADS" = "Gỡ quảng cáo";
"CAN_SAVE_VIDEO" = "Có thể tải video";
"LONG_PRESS_ON_VIDEO_TO_SAVE" = "Ấn và giữ video để lưu";
"REMOVE_ADS_IN_NEWS_FEED" = "Gỡ quảng cáo ở News Feed";
"OTHER_PREFERENCES" = "Tùy chỉnh phụ";
"RESET_SETTINGS" = "Đặt lại tùy chỉnh";
Expand Down
Binary file not shown.

0 comments on commit 0867fe6

Please sign in to comment.