├── .gitignore
├── DMHY.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── yaqinking.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── DMHY.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── xcuserdata
│ └── yaqinking.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── DMHY
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon_128x128.png
│ │ ├── icon_128x128@2x.png
│ │ ├── icon_16x16.png
│ │ ├── icon_16x16@2x.png
│ │ ├── icon_256x256.png
│ │ ├── icon_256x256@2x.png
│ │ ├── icon_32x32.png
│ │ ├── icon_32x32@2x.png
│ │ ├── icon_512x512.png
│ │ └── icon_512x512@2x.png
│ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Controller
│ ├── AddKeywordViewController.h
│ ├── AddKeywordViewController.m
│ ├── ExportKeywordViewController.h
│ ├── ExportKeywordViewController.m
│ ├── ImportKeywordViewController.h
│ └── ImportKeywordViewController.m
├── DMHY.entitlements
├── DMHY.xcdatamodeld
│ ├── .xccurrentversion
│ ├── DMHY v2.xcdatamodel
│ │ └── contents
│ ├── DMHY v3.xcdatamodel
│ │ └── contents
│ ├── DMHY v4.xcdatamodel
│ │ └── contents
│ └── DMHY.xcdatamodel
│ │ └── contents
├── DMHYAPI.h
├── DMHYCoreDataStackManager.h
├── DMHYCoreDataStackManager.m
├── DMHYDownloader.h
├── DMHYDownloader.m
├── DMHYJSONDataManager.h
├── DMHYJSONDataManager.m
├── DMHYNotification.h
├── DMHYNotification.m
├── DMHYSiteChecker.h
├── DMHYSiteChecker.m
├── DMHYTool.h
├── DMHYTool.m
├── DMHYXMLDataManager.h
├── DMHYXMLDataManager.m
├── FileManagerViewController.h
├── FileManagerViewController.m
├── Info.plist
├── MASPreferencesViewController.h
├── MASPreferencesWindow.xib
├── MASPreferencesWindowController.h
├── MASPreferencesWindowController.m
├── MainSplitViewController.h
├── MainSplitViewController.m
├── Model
│ ├── Bangumi.h
│ ├── Bangumi.m
│ ├── DMHYKeyword+CoreDataProperties.h
│ ├── DMHYKeyword+CoreDataProperties.m
│ ├── DMHYKeyword.h
│ ├── DMHYKeyword.m
│ ├── DMHYSite+CoreDataProperties.h
│ ├── DMHYSite+CoreDataProperties.m
│ ├── DMHYSite.h
│ ├── DMHYSite.m
│ ├── DMHYTorrent+CoreDataProperties.h
│ ├── DMHYTorrent+CoreDataProperties.m
│ ├── DMHYTorrent.h
│ ├── DMHYTorrent.m
│ ├── FileItem.h
│ └── FileItem.m
├── NavigationView.h
├── NavigationView.m
├── Preference.xib
├── PreferenceController.h
├── PreferenceController.m
├── Preferences
│ ├── SitePreferenceController.h
│ ├── SitePreferenceController.m
│ └── SitePreferenceController.xib
├── SavedDataViewController.h
├── SavedDataViewController.m
├── SideViewController.h
├── SideViewController.m
├── TitleTableCellView.h
├── TitleTableCellView.m
├── TorrentItem.h
├── TorrentItem.m
├── View
│ ├── ButtonTableCellView.h
│ ├── ButtonTableCellView.m
│ ├── FileTableView.h
│ ├── FileTableView.m
│ ├── NSTableView+ContextMenu.h
│ └── NSTableView+ContextMenu.m
├── ViewController.h
├── ViewController.m
├── ViewerPreferences.tiff
├── WindowController.h
├── WindowController.m
├── download.png
├── main.m
├── support-site.json
└── zh-Hans.lproj
│ └── Main.strings
├── LICENSE
├── Podfile
├── Podfile.lock
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | Pods/*
2 |
--------------------------------------------------------------------------------
/DMHY.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DMHY.xcodeproj/xcuserdata/yaqinking.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | DMHY.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 5
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/DMHY.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/DMHY.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DMHY.xcworkspace/xcuserdata/yaqinking.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY.xcworkspace/xcuserdata/yaqinking.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/DMHY/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/9/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : NSObject
12 |
13 | @property (nonatomic) NSWindowController *preferencesWindowController;
14 |
15 | - (IBAction)togglePreviewPanel:(id)previewPanel;
16 |
17 | @end
18 |
19 |
--------------------------------------------------------------------------------
/DMHY/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/9/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "PreferenceController.h"
11 | #import "SitePreferenceController.h"
12 | #import "DMHYCoreDataStackManager.h"
13 | #import "DMHYKeyword+CoreDataProperties.h"
14 | #import "DMHYAPI.h"
15 | #import "DMHYTorrent.h"
16 | #import "MASPreferencesWindowController.h"
17 |
18 | @import Quartz;
19 |
20 | @interface AppDelegate ()
21 |
22 |
23 | @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
24 |
25 | @end
26 |
27 | @implementation AppDelegate
28 |
29 | @synthesize managedObjectContext = _context;
30 |
31 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
32 |
33 | }
34 |
35 | - (void)applicationWillTerminate:(NSNotification *)aNotification {
36 | // Insert code here to tear down your application
37 | }
38 |
39 |
40 | #pragma mark - MenuItem
41 |
42 | - (IBAction)showPreference:(id)sender {
43 | [self.preferencesWindowController showWindow:nil];
44 | }
45 |
46 | - (IBAction)showDownloadPathInFinder:(id)sender {
47 | NSURL *savePath = [PreferenceController preferenceSavePath];
48 | [[NSWorkspace sharedWorkspace] openURL:savePath];
49 | }
50 |
51 | - (IBAction)deleteAllExistTorrents:(id)sender {
52 | NSFetchRequest *existTorrentRequest = [NSFetchRequest fetchRequestWithEntityName:@"Torrent"];
53 | // existTorrentRequest.predicate = [NSPredicate predicateWithFormat:@"title LIKE %@",@"传颂"];
54 | NSArray *torrents = [self.managedObjectContext executeFetchRequest:existTorrentRequest error:NULL];
55 | for (DMHYTorrent *torrent in torrents) {
56 | [self.managedObjectContext deleteObject:torrent];
57 | }
58 | [self.managedObjectContext save:NULL];
59 | }
60 |
61 | - (IBAction)switchTheme:(id)sender {
62 | NSInteger themeCode = [PreferenceController preferenceTheme];
63 | [PreferenceController setPreferenceTheme:(!themeCode)];
64 | [DMHYNotification postNotificationName:DMHYThemeChangedNotification];
65 | }
66 |
67 | - (IBAction)showHelp:(id)sender {
68 | [[NSWorkspace sharedWorkspace] openURL:[self helpURL]];
69 | }
70 |
71 | - (IBAction)togglePreviewPanel:(id)previewPanel
72 | {
73 | if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible])
74 | {
75 | [[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
76 | }
77 | else
78 | {
79 | [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
80 | }
81 | }
82 |
83 | - (NSURL *)helpURL {
84 | return [NSURL URLWithString:@"https://github.com/yaqinking/DMHY/wiki"];
85 | }
86 |
87 | #pragma mark - Properties Initialization
88 |
89 | - (NSWindowController *)preferencesWindowController
90 | {
91 | if (_preferencesWindowController == nil)
92 | {
93 | NSViewController *generalViewController = [[PreferenceController alloc] init];
94 | NSViewController *siteViewController = [[SitePreferenceController alloc] init];
95 |
96 | NSArray *controllers = @[generalViewController, siteViewController];
97 |
98 | // To add a flexible space between General and Advanced preference panes insert [NSNull null]:
99 | // NSArray *controllers = [[NSArray alloc] initWithObjects:generalViewController, [NSNull null], advancedViewController, nil];
100 |
101 | NSString *title = @"设置";
102 | _preferencesWindowController = [[MASPreferencesWindowController alloc] initWithViewControllers:controllers title:title];
103 |
104 | }
105 | return _preferencesWindowController;
106 | }
107 |
108 | - (NSManagedObjectContext *)managedObjectContext {
109 | if (!_context) {
110 | _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
111 | _context.persistentStoreCoordinator = [[DMHYCoreDataStackManager sharedManager] persistentStoreCoordinator];
112 | }
113 | return _context;
114 | }
115 |
116 | - (void)saveData {
117 | NSError *error = nil;
118 | if (![self.managedObjectContext save:&error]) {
119 | NSLog(@"Error %@",[error localizedDescription]);
120 | }
121 | }
122 |
123 | @end
124 |
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "icon_16x16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "icon_16x16@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "icon_32x32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "icon_32x32@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "icon_128x128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "icon_128x128@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "icon_256x256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "icon_256x256@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "icon_512x512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "icon_512x512@2x.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/DMHY/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/DMHY/Controller/AddKeywordViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // AddKeywordViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/17/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AddKeywordViewController : NSViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/Controller/AddKeywordViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // AddKeywordViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/17/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "AddKeywordViewController.h"
10 | #import "DMHYCoreDataStackManager.h"
11 |
12 | @interface AddKeywordViewController ()
13 |
14 | @property (weak) IBOutlet NSTextField *keywordTextField;
15 | @property (weak) IBOutlet NSPopUpButton *sitesPopUpButton;
16 | @property (weak) IBOutlet NSPopUpButton *weekdayPopUpButton;
17 |
18 | @end
19 |
20 | @implementation AddKeywordViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 | // Do view setup here.
25 | self.preferredContentSize = NSMakeSize(580, 140);
26 | [self setupData];
27 | }
28 |
29 | - (void)setupData {
30 | NSMutableArray *siteNames = [[NSMutableArray alloc] init];
31 | NSArray *sites = [[DMHYCoreDataStackManager sharedManager] allSites];
32 | [sites enumerateObjectsUsingBlock:^(DMHYSite * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
33 | [siteNames addObject:obj.name];
34 | }];
35 | [self.sitesPopUpButton addItemsWithTitles:siteNames];
36 | // A cheat at visual feeling
37 | NSArray *weekdays = @[@"周一", @"周二", @"周三", @"周四", @"周五", @"周六", @"周日", @"其他"];
38 | [self.weekdayPopUpButton addItemsWithTitles:weekdays];
39 | }
40 |
41 | - (IBAction)addKeyword:(id)sender {
42 | NSString *newKeyword = self.keywordTextField.stringValue;
43 | if ([newKeyword isEqualToString:@""]) {
44 | return;
45 | }
46 | NSMenuItem *siteItem = [self.sitesPopUpButton selectedItem];
47 | NSInteger siteIndex = [self.sitesPopUpButton indexOfItem:siteItem];
48 | DMHYSite *site = [[[DMHYCoreDataStackManager sharedManager] allSites] objectAtIndex:siteIndex];
49 |
50 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyword == %@", self.weekdayPopUpButton.selectedItem.title];
51 | DMHYKeyword *weekdayKeyword = [[[site.keywords allObjects] filteredArrayUsingPredicate:predicate] firstObject];
52 | DMHYKeyword *keyword = [NSEntityDescription insertNewObjectForEntityForName:DMHYKeywordEntityKey inManagedObjectContext:[[DMHYCoreDataStackManager sharedManager] managedObjectContext]];
53 | keyword.keyword = newKeyword;
54 | keyword.createDate = [NSDate new];
55 | keyword.isSubKeyword = @YES;
56 | [weekdayKeyword addSubKeywordsObject:keyword];
57 | [[DMHYCoreDataStackManager sharedManager] saveContext];
58 | [DMHYNotification postNotificationName:DMHYKeywordAddedNotification object:site];
59 | }
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/DMHY/Controller/ExportKeywordViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ExportViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/18/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ExportKeywordViewController : NSViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/Controller/ExportKeywordViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ExportViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/18/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "ExportKeywordViewController.h"
10 | #import "DMHYCoreDataStackManager.h"
11 |
12 | @interface ExportKeywordViewController ()
13 | @property (weak) IBOutlet NSStackView *stackView;
14 | @property (unsafe_unretained) IBOutlet NSTextView *textView;
15 |
16 |
17 | @property (nonatomic, strong) NSMutableArray *exportSiteNames;
18 | @end
19 |
20 | @implementation ExportKeywordViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 | // Do view setup here.
25 | self.textView.minSize = NSMakeSize(200, 400);
26 | NSArray *sites = [[DMHYCoreDataStackManager sharedManager] allSites];
27 |
28 | [sites enumerateObjectsUsingBlock:^(DMHYSite * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
29 | NSButton *button = [[NSButton alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
30 | [button setButtonType:NSSwitchButton];
31 | [button setBezelStyle:NSRoundedBezelStyle];
32 | button.title = obj.name;
33 | button.target = self;
34 | button.action = @selector(checkButtonChanged:);
35 | [self.stackView insertArrangedSubview:button atIndex:idx];
36 | }];
37 | self.exportSiteNames = [NSMutableArray new];
38 | }
39 |
40 | - (void)checkButtonChanged:(NSButton *)sender {
41 | if (sender.state == 1) {
42 | [self.exportSiteNames addObject:sender.title];
43 | } else {
44 | [self.exportSiteNames removeObject:sender.title];
45 | }
46 | }
47 |
48 | - (IBAction)export:(NSButton *)sender {
49 | [self.textView.textStorage.mutableString setString:@""];
50 | [[DMHYCoreDataStackManager sharedManager] exportSites:self.exportSiteNames success:^(NSString *json){
51 | [self.textView insertText:json];
52 | } failure:^(NSError *error) {
53 | [NSApp presentError:error];
54 | }];
55 | }
56 |
57 | - (IBAction)copy:(id)sender {
58 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
59 | [pasteboard clearContents];
60 | [pasteboard writeObjects:@[self.textView.string]];
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/DMHY/Controller/ImportKeywordViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImportKeywordViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/18/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ImportKeywordViewController : NSViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/Controller/ImportKeywordViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ImportKeywordViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/18/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "ImportKeywordViewController.h"
10 | #import "DMHYCoreDataStackManager.h"
11 |
12 | @interface ImportKeywordViewController ()
13 |
14 | @property (unsafe_unretained) IBOutlet NSTextView *jsonTextView;
15 | @property (weak) IBOutlet NSTextField *infoTextField;
16 |
17 |
18 | @end
19 |
20 | @implementation ImportKeywordViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 | self.preferredContentSize = NSMakeSize(617, 489);
25 | }
26 |
27 | - (IBAction)import:(id)sender {
28 | NSString *text = self.jsonTextView.textStorage.string;
29 | NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
30 | NSError *error = nil;
31 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
32 | if (error) {
33 | [NSApp presentError:error];
34 | return;
35 | }
36 | NSArray *sites = json[@"sites"];
37 | [[DMHYCoreDataStackManager sharedManager] importFromSites:sites success:^{
38 | [[DMHYCoreDataStackManager sharedManager] saveContext];
39 | self.infoTextField.stringValue = @"导入完毕";
40 | } failure:^(NSError *error) {
41 | self.infoTextField.stringValue = @"导入失败";
42 | }];
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/DMHY/DMHY.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.assets.movies.read-write
8 |
9 | com.apple.security.assets.music.read-write
10 |
11 | com.apple.security.assets.pictures.read-write
12 |
13 | com.apple.security.files.bookmarks.app-scope
14 |
15 | com.apple.security.files.downloads.read-write
16 |
17 | com.apple.security.files.user-selected.read-write
18 |
19 | com.apple.security.network.client
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/DMHY/DMHY.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | DMHY v4.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DMHY/DMHY.xcdatamodeld/DMHY v2.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/DMHY/DMHY.xcdatamodeld/DMHY v3.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/DMHY/DMHY.xcdatamodeld/DMHY v4.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/DMHY/DMHY.xcdatamodeld/DMHY.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/DMHY/DMHYAPI.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYAPI.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 15/8/31.
6 | // Copyright © 2015年 yaqinking. All rights reserved.
7 | //
8 |
9 | #ifndef DMHYAPI_h
10 | #define DMHYAPI_h
11 |
12 | #define kXpathTorrentDownloadShortURL @"//div[@class='dis ui-tabs-panel ui-widget-content ui-corner-bottom']/a/@href"
13 | #define kXpathTorrentDirectDownloadLink @"//div[@class='dis']//p//a//@href"
14 |
15 | #define kXPathTorrentItem @"//item"
16 | #define kDownloadLinkType @"DownloadLinkType"
17 | #define kDownloadSite @"DownloadSite"
18 | #define kSavePath @"SavePath"
19 | #define kFileWatchPath @"FileWatchPath"
20 | #define kSelectKeyword @"SelectKeyword"
21 | #define kSelectKeywordIsSubKeyword @"SelectKeywordIsSubKeyword"
22 | #define kWeekdayOther @"其他"
23 | #define kMainViewRowStyle @"MainViewRowStyle"
24 | #define kMainTableViewRowStyle @"MainTableViewRowStyle"
25 | #define kDoubleAction @"DoubleAction"
26 |
27 | #define DMHYMainTableViewRowStyleChangedNotification @"DMHYMainTableViewRowStyleChangedNotification"
28 | #define DMHYDoubleActionChangedNotification @"DMHYDoubleActionChangedNotification"
29 | #define kFetchInterval @"FetchInterval"
30 | #define kFetchIntervalMinimum 300 //5 minutes
31 | #define kFetchIntervalMaximun 43200 //12 hours
32 |
33 | #define kFileWatchInterval @"FileWatchInterval"
34 | #define kFileWatchIntervalMinimum 60 //1 minutes
35 | #define kFileWatchIntervalMaximum 43200
36 |
37 | #define DMHYThemeChangedNotification @"DMHYThemeChangedNotification"
38 | #define DMHYSavePathChangedNotification @"DMHYSavaPathChangedNotification"
39 | #define DMHYFileWatchPathChangedNotification @"DMHYFileWatchPathChangedNotification"
40 | #define DMHYDatabaseChangedNotification @"DMHYDatabaseChangedNotification"
41 | #define DMHYDownloadLinkTypeNotification @"DMHYDownloadLinkTypeNotification"
42 | #define DMHYAutoDownloadSiteChangedNotification @"DMHYAutoDownloadSiteChangedNotification"
43 | #define DMHYDoubleActionChangedNotification @"DMHYDoubleActionChangedNotification"
44 | #define DMHYSelectKeywordChangedNotification @"DMHYSelectKeywordChangedNotification"
45 | #define DMHYFetchIntervalChangedNotification @"DMHYFetchIntervalChangedNotification"
46 |
47 | #define DMHYMainTableViewRowStyleChangedNotification @"DMHYMainTableViewRowStyleChangedNotification"
48 | #define DMHYFileWatchIntervalChangedNotification @"DMHYFileWatchIntervalChangedNotification"
49 |
50 | #define DMHYThemeKey @"ThemeType"
51 |
52 | typedef NS_ENUM(NSInteger, DMHYThemeType) {
53 | DMHYThemeLight,
54 | DMHYThemeDark
55 | };
56 |
57 | typedef NS_ENUM(NSInteger, DMHYSiteType) {
58 | DMHYSiteDefault,
59 | DMHYSiteDandanplay,
60 | DMHYSiteACGGG,
61 | DMHYSiteBangumiMoe
62 | };
63 |
64 | static NSString * const AppDomain = @"com.yaqinking.DMHY";
65 |
66 | static NSString * const DMHYSiteEntityKey = @"Site";
67 | static NSString * const DMHYKeywordEntityKey = @"Keyword";
68 | static NSString * const DMHYTorrentEntityKey = @"Torrent";
69 |
70 | static NSString * const DMHYSiteNameKey = @"site_name";
71 | static NSString * const DMHYSiteMainURLKey = @"main_url";
72 | static NSString * const DMHYSiteSearchURLKey = @"search_url";
73 | static NSString * const DMHYSiteFliterKey = @"fliter_site";
74 | static NSString * const DMHYSiteAutoDLKey = @"auto_download";
75 | static NSString * const DMHYSiteDLTypeKey = @"download_type";
76 | static NSString * const DMHYSiteDLFinKey = @"download_fin";
77 | static NSString * const DMHYSiteResponseTypeKey = @"response_type";
78 | static NSString * const DMHYSiteCurrentUseKey = @"current_use";
79 | static NSString * const DMHYSiteKeywordsKey = @"keywords";
80 |
81 | static NSString * const DMHYKeywordAddedNotification = @"moe.yaqinking.dmhy.added.keyword.notification";
82 | static NSString * const DMHYSearchSiteChangedNotification = @"moe.yaqinking.dmhy.search.site.changed.notification";
83 | static NSString * const DMHYSiteAddedNotification = @"moe.yaqinking.dmhy.site.added.notification";
84 | static NSString * const DMHYKeywordCheckedNotification = @"moe.yaqinking.dmhy.keyword.checked.notification";
85 |
86 | #import "DMHYTool.h"
87 | #import "DMHYNotification.h"
88 | #import "DMHYDownloader.h"
89 |
90 | #endif /* DMHYAPI_h */
91 |
--------------------------------------------------------------------------------
/DMHY/DMHYCoreDataStackManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYCoreDataStackManager.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/22/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "DMHYAPI.h"
11 | #import "DMHYSite.h"
12 | #import "DMHYKeyword.h"
13 | #import "DMHYTorrent.h"
14 |
15 | extern NSString * const DMHYSeedDataCompletedNotification;
16 |
17 | @import Cocoa;
18 |
19 | @interface DMHYCoreDataStackManager : NSObject
20 |
21 | @property (nonatomic, readonly) NSManagedObjectModel *managedObjectModel;
22 | @property (nonatomic, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
23 | @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
24 |
25 | @property (nonatomic, readonly) NSURL *storeURL;
26 | @property (nonatomic, readonly) NSURL *applicationDocumentsDirectory;
27 |
28 | + (instancetype)sharedManager;
29 | - (BOOL)resetDatabase;
30 |
31 | - (void)seedDataIfNeed;
32 | - (void)saveContext;
33 |
34 |
35 |
36 | - (DMHYSite *)querySiteWithSiteName:(NSString *)siteName;
37 | - (void)addDefaultWeekdaysToSite:(DMHYSite *)site;
38 |
39 | /**
40 | * Insert multi keywords
41 | * (site->weeday->keywords)
42 | *
43 | * @param keywords Keywords array to insert
44 | * @param weekday The keywords is belong to this weekday
45 | * @param site The weekday belong to this site
46 | */
47 | - (void)insertKeywords:(NSArray *)keywords weekday:(NSString *)weekday site:(DMHYSite *) site;
48 |
49 | - (DMHYSite *)currentUseSite;
50 | - (NSArray *)autoDownloadSites;
51 | - (NSArray *)allSites;
52 |
53 | - (BOOL)existTorrentWithTitle:(NSString *)title;
54 |
55 | - (void)importFromSites:(NSArray *)sites success:(void(^)()) successHandler failure:(void(^)(NSError *error)) failureHandler;
56 | - (void)exportSites:(NSArray *)siteNames success:(void(^)(NSString *json)) successHandler failure:(void(^)(NSError *error)) failureHandler;
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/DMHY/DMHYDownloader.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYDownloader.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/24/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "DMHYAPI.h"
11 | #import "DMHYNotification.h"
12 | #import "AFNetworking.h"
13 | #import "PreferenceController.h"
14 | #import "Ono.h"
15 |
16 | /**
17 | * 下载给定链接的种子
18 | */
19 | @interface DMHYDownloader : NSObject
20 |
21 | + (DMHYDownloader *)downloader;
22 |
23 | /**
24 | * Download torrent with given url and push a local notification to notify use downloaded
25 | *
26 | * @param url Download file url
27 | */
28 | - (void)downloadTorrentWithURL:(NSURL *)url;
29 | /**
30 | * Download torrent from share.dmhy.org torrent description page
31 | * 1. Exract torrent download url
32 | * @param url dmhy bangumi description page
33 | */
34 | - (void)downloadTorrentFromPageURLString:(NSString *)urlString;
35 |
36 | - (void)downloadTorrentFromPageURLString:(NSString *)urlString willStartBlock:(void(^)()) startBlock success:(void(^)()) successHandler failure:(void(^)(NSError *error)) failureHandler;
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/DMHY/DMHYDownloader.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYDownloader.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/24/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYDownloader.h"
10 |
11 | @interface DMHYDownloader()
12 |
13 | @property (nonatomic, strong) AFURLSessionManager *manager;
14 |
15 | @end
16 |
17 | @implementation DMHYDownloader
18 |
19 | + (DMHYDownloader *)downloader {
20 | static DMHYDownloader *downloader = nil;
21 | static dispatch_once_t onceToken;
22 | dispatch_once(&onceToken, ^{
23 | downloader = [[DMHYDownloader alloc] init];
24 | });
25 | return downloader;
26 | }
27 |
28 | - (void)downloadTorrentWithURL:(NSURL *)url {
29 | NSURLRequest *request = [NSURLRequest requestWithURL:url];
30 | NSURLSessionDownloadTask *downloadTask = [self.manager downloadTaskWithRequest:request
31 | progress:nil
32 | destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
33 | NSURL *savePath = [PreferenceController preferenceSavePath];
34 | if (!savePath) {
35 | savePath = [PreferenceController userDownloadPath];
36 | }
37 | return [savePath URLByAppendingPathComponent:[response suggestedFilename]];
38 |
39 | } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error) {
40 | NSString *fileName = [response suggestedFilename];
41 | [self postUserNotificationWithFileName:fileName];
42 |
43 | }];
44 | [downloadTask resume];
45 | }
46 |
47 | - (void)downloadTorrentFromPageURLString:(NSString *)urlString {
48 | [self downloadTorrentFromPageURLString:urlString willStartBlock:nil success:nil failure:nil];
49 | }
50 |
51 | - (void)downloadTorrentFromPageURLString:(NSString *)urlString willStartBlock:(void (^)())startBlock success:(void (^)())successHandler failure:(void (^)(NSError *))failureHandler {
52 | if (startBlock) {
53 | startBlock();
54 | }
55 | AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
56 | manager.responseSerializer = [AFHTTPResponseSerializer serializer];
57 | [manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
58 | dispatch_async(dispatch_queue_create("download queue", nil), ^{
59 | ONOXMLDocument *doc = [ONOXMLDocument HTMLDocumentWithData:responseObject error:nil];
60 | __block NSMutableString *downloadString = [NSMutableString new];
61 | [doc enumerateElementsWithXPath:kXpathTorrentDirectDownloadLink usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) {
62 | downloadString = [[element stringValue] mutableCopy];
63 | *stop = YES;
64 | }];
65 |
66 | NSURL *dlURL = [NSURL URLWithString:[NSString stringWithFormat:@"https:%@",downloadString]];
67 | [self downloadTorrentWithURL:dlURL];
68 | if (successHandler) {
69 | successHandler();
70 | }
71 | });
72 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
73 | if (failureHandler) {
74 | failureHandler(error);
75 | }
76 | }];
77 | }
78 |
79 | #pragma mark - LocalNotification
80 |
81 | - (void)postUserNotificationWithFileName:(NSString *)fileName {
82 | NSUserNotification *notification = [[NSUserNotification alloc] init];
83 | notification.title = @"下载完成";
84 | notification.informativeText = fileName;
85 | notification.soundName = NSUserNotificationDefaultSoundName;
86 | [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
87 | }
88 |
89 | - (AFURLSessionManager *)manager {
90 | if (!_manager) {
91 | NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration];
92 | _manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:conf];
93 | }
94 | return _manager;
95 | }
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/DMHY/DMHYJSONDataManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYJSONManager.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/31/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class TorrentItem;
12 |
13 | extern NSString * const DMHYBangumiMoeOpenTorrentPagePrefixFormat;
14 | extern NSString * const publishTimeKey;
15 | extern NSString * const idKey;
16 | extern NSString * const teamNameKeyPath;
17 | extern NSString * const uploaderUserNameKeyPath;
18 | extern NSString * const magnetKey;
19 |
20 | typedef void (^DMHYJSONDataFetchSuccessBlock)(NSArray *objects);
21 | typedef void (^DMHYJSONDataFetchFailureBlock)(NSError *error);
22 |
23 | @interface DMHYJSONDataManager : NSObject
24 |
25 | + (DMHYJSONDataManager *)manager;
26 |
27 | - (void)GET:(NSString *)urlString success:(DMHYJSONDataFetchSuccessBlock) successBlock failure:(DMHYJSONDataFetchFailureBlock) failureBlock;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/DMHY/DMHYJSONDataManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYJSONManager.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/31/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYJSONDataManager.h"
10 | #import "AFNetworking.h"
11 | #import "TorrentItem.h"
12 |
13 | NSString * const DMHYBangumiMoeOpenTorrentPagePrefixFormat = @"https://bangumi.moe/torrent/%@";
14 | NSString * const publishTimeKey = @"publish_time";
15 | NSString * const idKey = @"_id";
16 | NSString * const teamNameKeyPath = @"team.name";
17 | NSString * const uploaderUserNameKeyPath = @"uploader.username";
18 | NSString * const magnetKey = @"magnet";
19 |
20 | @interface DMHYJSONDataManager()
21 |
22 | @property (nonatomic, strong) AFHTTPSessionManager *httpManager;
23 |
24 | @end
25 |
26 | @implementation DMHYJSONDataManager
27 |
28 | + (DMHYJSONDataManager *)manager {
29 | static DMHYJSONDataManager *sharedManager = nil;
30 | static dispatch_once_t onceToken;
31 | dispatch_once(&onceToken, ^{
32 | sharedManager = [[DMHYJSONDataManager alloc] init];
33 | });
34 | return sharedManager;
35 |
36 | }
37 |
38 | - (void)GET:(NSString *)urlString success:(DMHYJSONDataFetchSuccessBlock)successBlock failure:(DMHYJSONDataFetchFailureBlock)failureBlock {
39 | [self.httpManager GET:urlString
40 | parameters:nil progress:nil
41 | success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
42 | NSArray *torrentsArray = [responseObject valueForKey:@"torrents"];
43 | NSMutableArray *data = [NSMutableArray new];
44 | for (NSDictionary *element in torrentsArray) {
45 | TorrentItem *item = [[TorrentItem alloc] init];
46 | NSString *pubDate = element[publishTimeKey];
47 | NSString *link = element[idKey];
48 | NSString *team_name = [element valueForKeyPath:teamNameKeyPath];
49 | if (!team_name) {
50 | team_name = [element valueForKeyPath:uploaderUserNameKeyPath];
51 | }
52 | item.title = element[@"title"];
53 | item.magnet = [NSURL URLWithString:element[magnetKey]];
54 | item.link = [NSURL URLWithString:[NSString stringWithFormat:DMHYBangumiMoeOpenTorrentPagePrefixFormat,link]]; //如果返回的 URL 里没有前缀的话,在这里也要处理
55 | item.pubDate = pubDate;
56 | item.author = team_name;
57 | [data addObject:item];
58 | }
59 | successBlock(data);
60 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
61 | // NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
62 | NSLog(@"XMLDataManager Request Error %@",[error localizedDescription]);
63 | // NSNumber *statusCode = [NSNumber numberWithInteger:httpResponse.statusCode];
64 | failureBlock(error);
65 | }];
66 | }
67 |
68 | - (AFHTTPSessionManager *)httpManager {
69 | if (!_httpManager) {
70 | _httpManager = [AFHTTPSessionManager manager];
71 | _httpManager.responseSerializer = [AFJSONResponseSerializer serializer];
72 | }
73 | return _httpManager;
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/DMHY/DMHYNotification.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYNotification.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/23/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DMHYNotification : NSObject
12 |
13 | + (void)postNotificationName:(NSString *) name;
14 | + (void)postNotificationName:(NSString *)name userInfo:(id)info;
15 | + (void)postNotificationName:(NSString *)name object:(id)object;
16 | + (void)postNotificationName:(NSString *)name object:(id)object userInfo:(id)info;
17 | + (void)addObserver:(id)object selector:(SEL) selector name:(NSString *)name;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/DMHY/DMHYNotification.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYNotification.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/23/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYNotification.h"
10 |
11 | @implementation DMHYNotification
12 |
13 | + (void)postNotificationName:(NSString *)name {
14 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil];
15 | }
16 |
17 | + (void)postNotificationName:(NSString *)name userInfo:(id)info {
18 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:info];
19 | }
20 |
21 | + (void)postNotificationName:(NSString *)name object:(id)object {
22 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:object];
23 | }
24 |
25 | + (void)postNotificationName:(NSString *)name object:(id)object userInfo:(id)info {
26 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:info];
27 | }
28 |
29 | + (void)addObserver:(id)object selector:(SEL)selector name:(NSString *)name {
30 | [[NSNotificationCenter defaultCenter] addObserver:object
31 | selector:selector
32 | name:name
33 | object:nil];
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/DMHY/DMHYSiteChecker.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSiteChecker.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /**
12 | * 检查站点数据是否有更新
13 | */
14 | @interface DMHYSiteChecker : NSObject
15 |
16 | - (void)invalidateTimer;
17 | - (void)startWitchCheckInterval:(NSTimeInterval )checkInterval;
18 | - (void)automaticDownloadTorrent;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/DMHY/DMHYSiteChecker.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSiteChecker.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYSiteChecker.h"
10 | #import "DMHYTool.h"
11 | #import "DMHYAPI.h"
12 | #import "DMHYCoreDataStackManager.h"
13 | #import "AFNetworking.h"
14 | #import "Ono.h"
15 | #import "DMHYXMLDataManager.h"
16 | #import "DMHYJSONDataManager.h"
17 |
18 | @interface DMHYSiteChecker()
19 |
20 | @property (nonatomic, strong) NSTimer *timer;
21 | @property (nonatomic, strong) NSString *today;
22 | @property (nonatomic, strong) NSDateFormatter *dateFormater;
23 |
24 | @property (nonatomic) BOOL isMagnetLink;
25 | @property (nonatomic) BOOL dontDownloadCollection;
26 |
27 | @end
28 |
29 | @implementation DMHYSiteChecker
30 |
31 | - (void)invalidateTimer {
32 | [self.timer invalidate];
33 | self.timer = nil;
34 | }
35 |
36 |
37 | - (void)startWitchCheckInterval:(NSTimeInterval)checkInterval {
38 | if (checkInterval >= 300) {
39 | self.timer = [NSTimer scheduledTimerWithTimeInterval:checkInterval
40 | target:self
41 | selector:@selector(automaticDownloadTorrent)
42 | userInfo:nil
43 | repeats:YES];
44 | } else {
45 | NSString *reason = [NSString stringWithFormat:@"\nFetch interval value is invalid. Current %li", (long)checkInterval];
46 | NSString *suggestion = [NSString stringWithFormat:@"You can open Terminal.app type\n\ndefaults write %@ FetchInterval 300\n\nThen press Enter key.\nRestart app to fix it.", AppDomain];
47 | NSDictionary *userInfo = @{ NSLocalizedFailureReasonErrorKey : reason,
48 | NSLocalizedRecoverySuggestionErrorKey : suggestion};
49 | NSError *error = [NSError errorWithDomain:AppDomain code:4444 userInfo:userInfo];
50 | [NSApp presentError:error];
51 | [NSApp terminate:self];
52 | }
53 | }
54 |
55 | /**
56 | * Check wheather today has new torrent. 其它分类下的每次都 fetch。
57 | * Then invoke downloadNewTorrents:
58 | * @param weekday today string
59 | */
60 | - (void)automaticDownloadTorrent {
61 | NSDate *now = [NSDate new];
62 | NSCalendar* cal = [NSCalendar currentCalendar];
63 | NSDateComponents *com = [cal components:NSCalendarUnitWeekday fromDate:now];
64 | NSInteger weekdayToday = [com weekday];// 1 = Sunday, 2 = Monday, etc.
65 | self.today = [DMHYTool cn_weekdayFromWeekdayCode:weekdayToday];
66 |
67 | NSArray *sites = [[DMHYCoreDataStackManager sharedManager] autoDownloadSites];
68 | [sites enumerateObjectsUsingBlock:^(DMHYSite * _Nonnull site, NSUInteger idx, BOOL * _Nonnull stop) {
69 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyword == %@ OR keyword == %@", self.today, kWeekdayOther];
70 | NSArray *flitedKeywords = [[site.keywords allObjects] filteredArrayUsingPredicate:predicate];
71 | [flitedKeywords enumerateObjectsUsingBlock:^(DMHYKeyword * _Nonnull parentKeyword, NSUInteger idx, BOOL * _Nonnull stop) {
72 | [parentKeyword.subKeywords enumerateObjectsUsingBlock:^(DMHYKeyword * _Nonnull keyword, BOOL * _Nonnull stop) {
73 | if ([site.responseType isEqualToString:@"xml"]) {
74 | [[DMHYXMLDataManager manager] fetchFromSite:site queryKeyword:keyword fetchedNew:^(DMHYTorrent *torrent) {
75 | if ([site.name isEqualToString:@"dmhy"]) {
76 | [[DMHYDownloader downloader] downloadTorrentFromPageURLString:torrent.link];
77 | return;
78 | }
79 | if ([site.name containsString:@"nyaa.se"]) {
80 | [[DMHYDownloader downloader] downloadTorrentWithURL:[NSURL URLWithString:torrent.link]];
81 | return;
82 | } else {
83 | NSURL *url = [NSURL URLWithString:torrent.magnet];
84 | // acg.rip contains .torrent bt.acg.gg contains down.php
85 | if ([torrent.magnet containsString:@".torrent"] ||
86 | [torrent.magnet containsString:@"down.php"]) {
87 | [[DMHYDownloader downloader] downloadTorrentWithURL:url];
88 | // Think this wil place to downloader completion handler?
89 | torrent.isDownloaded = @YES;
90 | torrent.isNewTorrent = @NO;
91 | }
92 | if ([torrent.magnet containsString:@"magnet:?xt=urn:btih:"]) {
93 | [[NSWorkspace sharedWorkspace] openURL:url];
94 | torrent.isDownloaded = @YES;
95 | torrent.isNewTorrent = @NO;
96 | }
97 | }
98 | } completion:^{
99 | NSLog(@"Site %@ Keyword %@ Check completed.", site.name, keyword.keyword);
100 | // When all task complated save changes.
101 | [[DMHYCoreDataStackManager sharedManager] saveContext];
102 | [DMHYNotification postNotificationName:DMHYKeywordCheckedNotification userInfo:@{@"site": site.name,
103 | @"keyword": keyword.keyword}];
104 | } failure:^(NSError *error) {
105 | NSLog(@"Site %@ Keyword %@ Check error %@", site.name, keyword.keyword, [error localizedDescription]);
106 | }];
107 | }
108 | if ([site.responseType isEqualToString:@"json"]) {
109 |
110 | }
111 | }];
112 | }];
113 | }];
114 | }
115 |
116 |
117 | #pragma mark - Download
118 |
119 | - (void)openMagnetWith:(NSURL *)magnet {
120 | [[NSWorkspace sharedWorkspace] openURL:magnet];
121 | }
122 |
123 | @end
124 |
--------------------------------------------------------------------------------
/DMHY/DMHYTool.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTool.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/1/21.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DMHYTool : NSObject
12 |
13 | /**
14 | * Some useful tool
15 | *
16 | * @return DMHYToll shared instance
17 | */
18 | + (DMHYTool *)tool;
19 |
20 | /**
21 | * Get chinese weekday.
22 | * 1 周日 2 周一 3 周二 4 周三 5 周四 6 周五 7 周日 0 周六 -1 周五
23 | * @param weekday the weekday code. -1 to 7
24 | *
25 | * @return Chinese weekday string.
26 | */
27 | + (NSString *) cn_weekdayFromWeekdayCode:(NSInteger )weekday;
28 |
29 | /**
30 | * Convert a url string to valided URL.
31 | *
32 | * @param urlString The url string maybe contain chinese.
33 | *
34 | * @return The valided URL.
35 | */
36 | + (NSURL *)convertToURLWithURLString:(NSString *)urlString;
37 |
38 | /**
39 | * Get bangumi season by month
40 | *
41 | * @param month The current month code
42 | *
43 | * @return belong to season
44 | */
45 | + (NSString *) bangumiSeasonOfMonth:(NSInteger )month;
46 |
47 | /**
48 | * Get nice looked date string from dmhy original date string
49 | *
50 | * @param dateString
51 | *
52 | * @return Nice looked date string
53 | */
54 | - (NSString *)formatedDateStringFromDMHYDateString:(NSString *)dateString;
55 |
56 | /**
57 | * Get date from dateString
58 | *
59 | * @param dateString
60 | *
61 | * @return A formated date
62 | */
63 | - (NSDate *)formatedDateFromDMHYDateString:(NSString *)dateString;
64 |
65 |
66 | - (NSString *)stringFromSavedDate:(NSDate *)date;
67 | - (NSString *)infoDateStringFromDate:(NSDate *)date;
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/DMHY/DMHYTool.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTool.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/1/21.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYTool.h"
10 | #import "NSDate+DateTools.h"
11 |
12 | NSInteger const kWeekTimeInterval = -7*24*60*60;
13 |
14 | @interface DMHYTool ()
15 |
16 | @property (nonatomic, strong) NSDateFormatter *dateFormater;
17 |
18 | @end
19 |
20 | @implementation DMHYTool
21 |
22 | + (DMHYTool *)tool {
23 | static DMHYTool *tool = nil;
24 | static dispatch_once_t onceToken;
25 | dispatch_once(&onceToken, ^{
26 | tool = [[DMHYTool alloc] init];
27 | });
28 | return tool;
29 | }
30 |
31 | + (NSString *) cn_weekdayFromWeekdayCode:(NSInteger )weekday {
32 | switch (weekday) {
33 | case 1:
34 | return @"周日";
35 | case 2:
36 | return @"周一";
37 | case 3:
38 | return @"周二";
39 | case 4:
40 | return @"周三";
41 | case 5:
42 | return @"周四";
43 | case 6:
44 | return @"周五";
45 | case 7:
46 | return @"周六";
47 | // for bgmlist 6 代表周六 但 JP 上映时间是 晚上 0000 之前的话,CN 则是 周日,然后取出值之后 +2 则是通用从 bgmlist 周几转到 dmhy 周几的 code。所以周六 bgmlist code 6 而 +2 之后是 8 但是是周日中国上映,8 在 KeywordViewController 里的bgmlist weekdaycode 中代表是 周日。
48 | case 8:
49 | return @"周日";
50 | // for yesterday and day before yesterday 1-1=0 1-2=-1 2-2=0;
51 | case 0:
52 | return @"周六";
53 | case -1:
54 | return @"周五";
55 | default:
56 | break;
57 | }
58 | return @"";
59 | }
60 |
61 | + (NSURL *)convertToURLWithURLString:(NSString *)urlString {
62 | NSCharacterSet *set = [NSCharacterSet URLQueryAllowedCharacterSet];
63 | NSString *escapedString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:set];
64 | return [NSURL URLWithString:escapedString];
65 | }
66 |
67 | + (NSString *)bangumiSeasonOfMonth:(NSInteger )month {
68 | switch (month) {
69 | case 1:
70 | case 2:
71 | case 3:
72 | return @"01";
73 | case 4:
74 | case 5:
75 | case 6:
76 | return @"04";
77 | case 7:
78 | case 8:
79 | case 9:
80 | return @"07";
81 | case 10:
82 | case 11:
83 | case 12:
84 | return @"10";
85 | default:
86 | break;
87 | }
88 | return @"";
89 | }
90 |
91 | - (NSString *)formatedDateStringFromDMHYDateString:(NSString *)dateString {
92 | // NSLog(@"dateFormater %@",self.dateFormater);
93 | self.dateFormater.dateFormat = @"EEE, dd MM yyyy HH:mm:ss Z";
94 | self.dateFormater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
95 | NSDate *longDate = [self.dateFormater dateFromString:dateString];
96 | NSDate *oneWeekAgo = [NSDate dateWithTimeIntervalSinceNow:kWeekTimeInterval];
97 | if ( [longDate isLaterThanOrEqualTo:oneWeekAgo]) {
98 | return longDate.timeAgoSinceNow;
99 | } else {
100 | self.dateFormater.dateFormat = @"EEE HH:mm:ss yy-MM-dd";
101 | self.dateFormater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
102 | return [self.dateFormater stringFromDate:longDate];
103 | }
104 | }
105 |
106 | - (NSDate *)formatedDateFromDMHYDateString:(NSString *)dateString {
107 | self.dateFormater.dateFormat = @"EEE, dd MM yyyy HH:mm:ss Z";
108 | self.dateFormater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
109 | NSDate *longDate = [self.dateFormater dateFromString:dateString];
110 | return longDate;
111 | }
112 |
113 | - (NSString *)stringFromSavedDate:(NSDate *)date {
114 | self.dateFormater.dateFormat = @"EEE HH:mm:ss yy-MM-dd";
115 | self.dateFormater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
116 | return [self.dateFormater stringFromDate:date];
117 | }
118 |
119 | - (NSString *)infoDateStringFromDate:(NSDate *)date {
120 | self.dateFormater.dateFormat = @"MM月dd日 HH:mm:ss";
121 | self.dateFormater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
122 | return [self.dateFormater stringFromDate:date];
123 | }
124 |
125 | - (NSDateFormatter *)dateFormater {
126 | if (!_dateFormater) {
127 | _dateFormater = [[NSDateFormatter alloc] init];
128 | }
129 | return _dateFormater;
130 | }
131 |
132 | @end
133 |
--------------------------------------------------------------------------------
/DMHY/DMHYXMLDataManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYXMLDataManager.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/31/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "DMHYCoreDataStackManager.h"
11 |
12 | @class TorrentItem;
13 |
14 | extern NSString * const pubDateKey;
15 | extern NSString * const titleKey;
16 | extern NSString * const linkKey;
17 | extern NSString * const authorKey;
18 | extern NSString * const kXPathItem;
19 |
20 | typedef void (^DMHYXMLDataFetchSuccessBlock)(NSArray *objects);
21 | typedef void (^DMHYXMLDataFetchFailureBlock)(NSError *error);
22 | typedef void (^DMHYXMLDataFetchedNewTorrentBlock)(DMHYTorrent *torrent);
23 | typedef void (^DMHYXMLDataFetchCompletionBlock)();
24 |
25 | @interface DMHYXMLDataManager : NSObject
26 |
27 | @property (nonatomic, readonly) DMHYXMLDataFetchSuccessBlock successBlock;
28 | @property (nonatomic, readonly) DMHYXMLDataFetchFailureBlock failureBlock;
29 |
30 | + (DMHYXMLDataManager *)manager;
31 |
32 | - (void)GET:(NSString *)urlString success:(DMHYXMLDataFetchSuccessBlock) successBlock failure:(DMHYXMLDataFetchFailureBlock) failureBlock;
33 | - (void)fetchFromSite:(DMHYSite *) site queryKeyword:(DMHYKeyword *)keyword fetchedNew:(DMHYXMLDataFetchedNewTorrentBlock) block completion:(DMHYXMLDataFetchCompletionBlock) completionHandler failure:(DMHYXMLDataFetchFailureBlock) failureHandler;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/DMHY/DMHYXMLDataManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYXMLDataManager.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/31/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYXMLDataManager.h"
10 | #import "AFNetworking.h"
11 | #import "Ono.h"
12 | #import "TorrentItem.h"
13 | #import "DMHYTool.h"
14 |
15 | NSString * const pubDateKey = @"pubDate";
16 | NSString * const titleKey = @"title";
17 | NSString * const linkKey = @"link";
18 | NSString * const authorKey = @"author";
19 |
20 | NSString * const kXPathItem = @"//item";
21 |
22 | @interface DMHYXMLDataManager()
23 |
24 | @property (nonatomic, strong) NSSet *accepableContentTypes;
25 | @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
26 |
27 | @end
28 |
29 | @implementation DMHYXMLDataManager
30 |
31 | + (DMHYXMLDataManager *)manager {
32 | static DMHYXMLDataManager *sharedManager = nil;
33 | static dispatch_once_t onceToken;
34 | dispatch_once(&onceToken, ^{
35 | sharedManager = [[DMHYXMLDataManager alloc] init];
36 | });
37 | return sharedManager;
38 | }
39 | - (void)GET:(NSString *)urlString success:(DMHYXMLDataFetchSuccessBlock)successBlock failure:(DMHYXMLDataFetchFailureBlock)failureBlock {
40 | _successBlock = successBlock;
41 | _failureBlock = failureBlock;
42 | AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
43 | manager.responseSerializer = [AFXMLDocumentResponseSerializer serializer];
44 | manager.responseSerializer.acceptableContentTypes = self.accepableContentTypes;
45 | __block NSMutableArray *torrents = [NSMutableArray new];
46 |
47 | [manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
48 | ONOXMLDocument *xmlDoc = [ONOXMLDocument XMLDocumentWithString:[responseObject description] encoding:NSUTF8StringEncoding error:nil];
49 | [xmlDoc enumerateElementsWithXPath:kXPathItem usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) {
50 | TorrentItem *item = [[TorrentItem alloc] init];
51 | NSString *dateString = [[element firstChildWithTag:pubDateKey] stringValue];
52 | NSString *pubDate = [[DMHYTool tool] formatedDateStringFromDMHYDateString:dateString];
53 | item.pubDate = pubDate ? pubDate : @"";
54 | item.title = [[element firstChildWithTag:titleKey] stringValue];
55 | item.link = [NSURL URLWithString:[[element firstChildWithTag:linkKey] stringValue]];
56 | NSString *author = [[element firstChildWithTag:authorKey] stringValue];
57 | item.author = author ? author : @"";
58 | NSString *magnetXPath = [NSString stringWithFormat:@"//item[%lu]//enclosure/@url", (idx+1)];
59 | NSString *magStr = [[element firstChildWithXPath:magnetXPath] stringValue];
60 | item.magnet = [NSURL URLWithString:magStr];
61 | [torrents addObject:item];
62 | }];
63 | _successBlock(torrents);
64 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
65 | _failureBlock(error);
66 | }];
67 | }
68 |
69 | - (void)fetchFromSite:(DMHYSite *)site queryKeyword:(DMHYKeyword *)keyword fetchedNew:(DMHYXMLDataFetchedNewTorrentBlock)block completion:(DMHYXMLDataFetchCompletionBlock)completionHandler failure:(DMHYXMLDataFetchFailureBlock)failureHandler {
70 | NSString *url = [[NSString stringWithFormat:site.searchURL, keyword.keyword] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
71 | NSLog(@"AutoDL URL %@", url);
72 | AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
73 | manager.responseSerializer = [AFXMLDocumentResponseSerializer serializer];
74 | manager.responseSerializer.acceptableContentTypes = self.accepableContentTypes;
75 | [manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
76 | ONOXMLDocument *xmlDoc = [ONOXMLDocument XMLDocumentWithString:[responseObject description] encoding:NSUTF8StringEncoding error:nil];
77 | [xmlDoc enumerateElementsWithXPath:kXPathTorrentItem usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) {
78 | NSString *title = [[element firstChildWithTag:@"title"] stringValue];
79 | /*
80 | if (self.dontDownloadCollection) {
81 | if ([title containsString:@"合集"] ||
82 | [title containsString:@"全集"]) {
83 | return;
84 | }
85 | }
86 | NSString *fliter = [[NSUserDefaults standardUserDefaults] stringForKey:FliterKeywordKey];
87 |
88 | if (![fliter isEqualToString:@""] && (fliter.length != 0)) {
89 | __block NSMutableString *containFliterResult = [NSMutableString new];
90 | NSArray *flites = [fliter componentsSeparatedByString:@" "];
91 | [flites enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
92 | if ([title containsString:obj]) {
93 | [containFliterResult appendString:@"1"];
94 | } else {
95 | [containFliterResult appendString:@"0"];
96 | }
97 | }];
98 | if ([containFliterResult containsString:@"0"]) {
99 | return ;
100 | }
101 | }
102 | */
103 | NSFetchRequest *requestExistTorrent = [NSFetchRequest fetchRequestWithEntityName:@"Torrent"];
104 | requestExistTorrent.predicate = [NSPredicate predicateWithFormat:@"keyword == %@ AND title == %@",keyword ,title];
105 | NSArray *existsTorrents = [self.managedObjectContext executeFetchRequest:requestExistTorrent
106 | error:NULL];
107 | if (!existsTorrents.count) {
108 | // NSLog(@"Didn't exist %@",title);
109 | DMHYTorrent *item = [NSEntityDescription insertNewObjectForEntityForName:@"Torrent"
110 | inManagedObjectContext:self.managedObjectContext];
111 | NSString *dateString = [[element firstChildWithTag:@"pubDate"] stringValue];
112 | item.pubDate = [[DMHYTool tool] formatedDateFromDMHYDateString:dateString];
113 | item.title = [[element firstChildWithTag:@"title"] stringValue];
114 | item.link = [[element firstChildWithTag:@"link"] stringValue];
115 | item.author = [[element firstChildWithTag:@"author"] stringValue];
116 | NSString *magnetXPath = [NSString stringWithFormat:@"//item[%lu]//enclosure/@url", (idx+1)];
117 | NSString *magStr = [[element firstChildWithXPath:magnetXPath] stringValue];
118 | item.magnet = magStr;
119 | item.isNewTorrent = @YES;
120 | item.isDownloaded = @NO;
121 | item.keyword = keyword;
122 | // NSLog(@"[New %@]",item.title);
123 | block(item);
124 | } else {
125 | // NSLog(@"Exist %@", title);
126 | *stop = 1;
127 | }
128 | }];
129 | // NSLog(@"XML Document Enumerate Completed inform completion block");
130 | completionHandler();
131 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
132 | failureHandler(error);
133 | }];
134 | }
135 |
136 | - (NSManagedObjectContext *)managedObjectContext {
137 | if (!_managedObjectContext) {
138 | _managedObjectContext = [[DMHYCoreDataStackManager sharedManager] managedObjectContext];
139 | }
140 | return _managedObjectContext;
141 | }
142 |
143 | - (NSSet *)accepableContentTypes {
144 | if (!_accepableContentTypes) {
145 | _accepableContentTypes = [NSSet setWithObjects:@"application/rss+xml",@"text/xml",@"application/xml", nil];
146 | }
147 | return _accepableContentTypes;
148 | }
149 |
150 | @end
151 |
--------------------------------------------------------------------------------
/DMHY/FileManagerViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // FileManagerViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/25/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface FileManagerViewController : NSViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/FileManagerViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // FileManagerViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/25/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "FileManagerViewController.h"
10 | #import "DMHYAPI.h"
11 | #import "PreferenceController.h"
12 | #import "AppDelegate.h"
13 | #import "NSTableView+ContextMenu.h"
14 | #import "FileItem.h"
15 | #import "FileTableView.h"
16 | #import "CDEvent.h"
17 | #import "CDEvents.h"
18 | #import
19 |
20 | @import Quartz;
21 |
22 | // QuickLook code from https://developer.apple.com/library/mac/samplecode/QuickLookDownloader/
23 |
24 | @interface FileItem (QLPreviewItem)
25 |
26 | @end
27 |
28 | @implementation FileItem (QLPreviewItem)
29 |
30 | - (NSURL *)previewItemURL {
31 | return self.url;
32 | }
33 |
34 | - (NSString *)previewItemTitle {
35 | return self.fileName;
36 | }
37 |
38 | @end
39 |
40 | @interface FileManagerViewController ()
41 |
42 | @property (weak) IBOutlet FileTableView *tableView;
43 | @property (weak) IBOutlet NSTextField *infoTextField;
44 |
45 | @property (strong) QLPreviewPanel *previewPanel;
46 |
47 | @property (nonatomic, strong) NSMutableArray *files;
48 | @property (nonatomic, strong) NSTimer *timer;
49 |
50 | @property (nonatomic, strong) NSURL *fileWatchURL;
51 |
52 | @property (nonatomic, strong) NSFileManager *fileManager;
53 | @property (nonatomic, strong) NSDateFormatter *formater;
54 |
55 | @property (nonatomic, strong) CDEvents *events;
56 |
57 | @end
58 |
59 | @implementation FileManagerViewController
60 |
61 | - (void)viewDidLoad {
62 | [super viewDidLoad];
63 | [self observeNotification];
64 | if (self.fileWatchURL) {
65 | [self setupFileData];
66 | [self setupFileWatcher];
67 | } else {
68 | self.infoTextField.stringValue = @"请在设定里设置文件监控路径 >_<";
69 | }
70 | [self setupTableViewDoubleAction];
71 | self.title = NSLocalizedString(@"File Manager", @"");
72 | }
73 |
74 | - (void)setupTableViewDoubleAction {
75 | self.tableView.doubleAction = @selector(openLocalFile:);
76 | }
77 |
78 | - (void)setupFileWatcher {
79 | CDEventsEventStreamCreationFlags creationFlags = kCDEventsDefaultEventStreamFlags;
80 |
81 | creationFlags |= kFSEventStreamCreateFlagFileEvents;
82 |
83 |
84 | _events = [[CDEvents alloc] initWithURLs:@[self.fileWatchURL]
85 | delegate:self
86 | onRunLoop:[NSRunLoop mainRunLoop]
87 | sinceEventIdentifier:kCDEventsSinceEventNow
88 | notificationLantency:CD_EVENTS_DEFAULT_NOTIFICATION_LATENCY
89 | ignoreEventsFromSubDirs:CD_EVENTS_DEFAULT_IGNORE_EVENT_FROM_SUB_DIRS
90 | excludeURLs:0
91 | streamCreationFlags:creationFlags];
92 | }
93 |
94 | - (void)setupTableViewStyle {
95 | self.tableView.rowSizeStyle = [self preferedRowSizeStyle];
96 | self.tableView.usesAlternatingRowBackgroundColors = YES;
97 | self.tableView.columnAutoresizingStyle = NSTableViewUniformColumnAutoresizingStyle;
98 | [self.tableView sizeLastColumnToFit];
99 | }
100 |
101 | - (NSTableViewRowSizeStyle )preferedRowSizeStyle {
102 | NSInteger rowStyle = [PreferenceController viewPreferenceTableViewRowStyle];
103 | switch (rowStyle) {
104 | case 0:
105 | return NSTableViewRowSizeStyleSmall;
106 | case 1:
107 | return NSTableViewRowSizeStyleMedium;
108 | case 2:
109 | return NSTableViewRowSizeStyleLarge;
110 | default:
111 | break;
112 | }
113 | return NSTableViewRowSizeStyleSmall;
114 | }
115 |
116 | - (NSURL *)fileWatchURL {
117 | if (!_fileWatchURL) {
118 | _fileWatchURL = [PreferenceController fileWatchPath];
119 | [_fileWatchURL startAccessingSecurityScopedResource];
120 | }
121 | return _fileWatchURL;
122 | }
123 |
124 | //const UInt32 DMHYFileEventFlagFileChanged = 67584;
125 | typedef NS_ENUM(UInt32, DMHYFileEventFlag) {
126 | DMHYFileEventFlagFileMoved = 128256,
127 | DMHYFileEventFlagFileChanged = 67584,
128 | DMHYFileEventFlagFileDeleted = 70656,
129 | DMHYFileEventFlagDirectoryChanged = 163840
130 | };
131 |
132 | - (void)URLWatcher:(CDEvents *)urlWatcher eventOccurred:(CDEvent *)event {
133 | if (event.flags == DMHYFileEventFlagFileChanged ||
134 | event.flags == DMHYFileEventFlagFileMoved ||
135 | event.flags == DMHYFileEventFlagDirectoryChanged) {
136 | [self setupFileData];
137 | }
138 |
139 | }
140 |
141 | #pragma mark - File Watch
142 |
143 | - (void)setupFileData {
144 | // NSLog(@"Setup File Data");
145 | dispatch_queue_t file_queue = dispatch_queue_create("file_watch_queue", 0);
146 | dispatch_async(file_queue, ^{
147 | // NSLog(@"File Manager %p File Watch URL %p",self.fileManager, self.fileWatchURL);
148 | if (!self.fileWatchURL) { //Stop when use didn't select directory.
149 | // NSLog(@"Not set watch directory.");
150 | return ;
151 | }
152 |
153 | NSDirectoryEnumerator *enumerator = [self.fileManager enumeratorAtURL:self.fileWatchURL
154 | includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
155 | options:NSDirectoryEnumerationSkipsHiddenFiles
156 | errorHandler:^BOOL(NSURL *url, NSError *error)
157 | {
158 | if (error) {
159 | NSLog(@"[Error] %@ (%@)", error, url);
160 | return NO;
161 | }
162 |
163 | return YES;
164 | }];
165 |
166 | NSMutableArray *mutableFileURLs = [NSMutableArray array];
167 | NSMutableArray *mutableFileNames = [NSMutableArray array];
168 | for (NSURL *fileURL in enumerator) {
169 | NSString *filename;
170 | [fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil];
171 |
172 | NSNumber *isDirectory;
173 | [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
174 | // Skip directories with '_' prefix, for example
175 | if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) {
176 | [enumerator skipDescendants];
177 | continue;
178 | }
179 |
180 | if (![isDirectory boolValue]) {
181 | [mutableFileURLs addObject:fileURL];
182 | [mutableFileNames addObject:filename];
183 | }
184 | }
185 |
186 | __block NSMutableArray *tempArr = [NSMutableArray new];
187 | __block NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:-60*60*24*7];
188 | [mutableFileURLs enumerateObjectsUsingBlock:^(id _Nonnull filePath, NSUInteger idx, BOOL * _Nonnull stop) {
189 | NSURL *url = filePath;
190 | NSString *extenstion = url.pathExtension;
191 | if ([extenstion isEqualToString:@"mp4"] ||
192 | [extenstion isEqualToString:@"mkv"] ) {
193 | if ([self.fileManager fileExistsAtPath:url.path]) {
194 | NSDictionary *attributes = [self.fileManager attributesOfItemAtPath:url.path error:nil];
195 | NSDate *modi = attributes[NSFileModificationDate];
196 | if ([modi compare:today] == NSOrderedDescending) {
197 | FileItem *item = [[FileItem alloc] initWithURL:url
198 | fileName:mutableFileNames[idx]
199 | modifyDate:modi];
200 | [tempArr addObject:item];
201 | }
202 | }
203 | }
204 | }];
205 | // Didn't stop accessing in order to preview video
206 | // [fileWatchURL stopAccessingSecurityScopedResource];
207 | NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"modifyDate" ascending:NO];
208 | self.files = [[tempArr sortedArrayUsingDescriptors:@[descriptor]] mutableCopy];
209 | dispatch_async(dispatch_get_main_queue(), ^{
210 | [self.tableView reloadData];
211 | [self.previewPanel reloadData];
212 | [self setupTableViewStyle];
213 | [self.tableView setNeedsDisplay:YES];
214 | NSDate *time = [NSDate new];
215 | self.infoTextField.stringValue = [NSString stringWithFormat:@"文件列表载入时间:%@",[[DMHYTool tool] infoDateStringFromDate:time]];
216 | });
217 | });
218 |
219 | }
220 |
221 | #pragma mark - Table View DataSource
222 |
223 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
224 | return self.files.count;
225 | }
226 |
227 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
228 | NSString *identifier = tableColumn.identifier;
229 | FileItem *file = self.files[row];
230 | if ([identifier isEqualToString:@"modifyDateCell"]) {
231 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"modifyDateCell" owner:self];
232 | NSDate *modi = file.modifyDate;
233 | NSString *cDate = [[DMHYTool tool] stringFromSavedDate:modi];
234 | cellView.textField.stringValue = cDate;
235 | return cellView;
236 | }
237 | if ([identifier isEqualToString:@"fileNameCell"]) {
238 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"fileNameCell" owner:self];
239 | cellView.textField.stringValue = file.fileName;
240 | return cellView;
241 | }
242 | return nil;
243 | }
244 |
245 | - (NSDateFormatter *)formater {
246 | if (!_formater) {
247 | _formater = [NSDateFormatter new];
248 | _formater.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
249 | _formater.dateFormat = @"EEE HH:mm:ss yy-MM-dd";
250 | }
251 | return _formater;
252 | }
253 |
254 | #pragma mark - Context Menu Delegate
255 |
256 | - (NSMenu *)tableView:(NSTableView *)aTableView menuForRows:(NSIndexSet *)rows {
257 | NSMenu *rightClickMenu = [[NSMenu alloc] initWithTitle:@""];
258 | NSMenuItem *openItem = [[NSMenuItem alloc] initWithTitle:@"打开"
259 | action:@selector(openLocalFile:)
260 | keyEquivalent:@""];
261 | NSMenuItem *downloadItem = [[NSMenuItem alloc] initWithTitle:@"在 Finder 中显示"
262 | action:@selector(locateInFinder:)
263 | keyEquivalent:@""];
264 | [rightClickMenu addItem:openItem];
265 | [rightClickMenu addItem:downloadItem];
266 | return rightClickMenu;
267 | }
268 |
269 | #pragma mark - Utils
270 |
271 | - (void)locateInFinder:(id) sender {
272 | NSInteger selectRow = self.tableView.selectedRow;
273 | FileItem *file = self.files[selectRow];
274 | [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[file.url]];
275 | }
276 |
277 | - (void)openLocalFile:(id)sender {
278 | NSInteger selectRow = self.tableView.selectedRow;
279 | if (selectRow < 0) {
280 | return;
281 | }
282 | FileItem *file = self.files[selectRow];
283 | [[NSWorkspace sharedWorkspace] openURL:file.url];
284 | }
285 |
286 | - (void)observeNotification {
287 | [DMHYNotification addObserver:self selector:@selector(handleThemeChanged) name:DMHYThemeChangedNotification];
288 | [DMHYNotification addObserver:self selector:@selector(handleFileWatchPathChanged) name:DMHYFileWatchPathChangedNotification];
289 | [DMHYNotification addObserver:self selector:@selector(handleMainTableViewRowStyleChanged) name:DMHYMainTableViewRowStyleChangedNotification];
290 | }
291 |
292 | - (void)handleFileWatchPathChanged {
293 | self.files = nil;
294 | self.fileWatchURL = nil;
295 | [self setupFileData];
296 | }
297 |
298 | - (void)handleMainTableViewRowStyleChanged {
299 | [self setupTableViewStyle];
300 | [self.tableView setNeedsDisplay:YES];
301 | }
302 |
303 | - (void)handleThemeChanged {
304 | [self.view setNeedsDisplay:YES];
305 | }
306 |
307 | - (NSMutableArray *)files {
308 | if (!_files) {
309 | _files = [NSMutableArray new];
310 | }
311 | return _files;
312 | }
313 |
314 | - (NSFileManager *)fileManager {
315 | if (!_fileManager) {
316 | _fileManager = [NSFileManager defaultManager];
317 | }
318 | return _fileManager;
319 | }
320 |
321 | #pragma mark - Quick Look panel support
322 |
323 | - (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel {
324 | return YES;
325 | }
326 |
327 | - (void)beginPreviewPanelControl:(QLPreviewPanel *)panel {
328 | _previewPanel = panel;
329 | panel.delegate = self;
330 | panel.dataSource = self;
331 | }
332 |
333 | - (void)endPreviewPanelControl:(QLPreviewPanel *)panel {
334 | _previewPanel = nil;
335 | }
336 |
337 | #pragma mark - QLPreviewPanelDataSource
338 |
339 | - (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel {
340 | return self.files.count;
341 | }
342 |
343 | - (id )previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index {
344 | NSInteger selectRow = self.tableView.selectedRow;
345 | return self.files[selectRow];
346 | }
347 |
348 | #pragma mark - QLPreviewPanelDelegate
349 |
350 | - (BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {
351 | // redirect all key down events to the table view
352 | if ([event type] == NSKeyDown) {
353 | [self.tableView keyDown:event];
354 | return YES;
355 | }
356 | return NO;
357 | }
358 |
359 | // This delegate method provides the rect on screen from which the panel will zoom.
360 | - (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id )item {
361 | NSInteger index = [self.files indexOfObject:item];
362 | if (index == NSNotFound) {
363 | return NSZeroRect;
364 | }
365 |
366 | NSRect iconRect = [self.tableView frameOfCellAtColumn:0 row:index];
367 |
368 | // check that the icon rect is visible on screen
369 | NSRect visibleRect = [self.tableView visibleRect];
370 |
371 | if (!NSIntersectsRect(visibleRect, iconRect)) {
372 | return NSZeroRect;
373 | }
374 |
375 | // convert icon rect to screen coordinates
376 | iconRect = [self.tableView convertRectToBacking:iconRect];
377 | NSRect test = [[self.tableView window] convertRectToScreen:iconRect];
378 | iconRect.origin = test.origin;
379 |
380 | return iconRect;
381 | }
382 |
383 | @end
384 |
--------------------------------------------------------------------------------
/DMHY/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSApplicationCategoryType
24 | public.app-category.utilities
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | NSHumanReadableCopyright
33 | Copyright © 2016-2021 yaqinking
34 | All rights reserved.
35 | NSMainStoryboardFile
36 | Main
37 | NSPrincipalClass
38 | NSApplication
39 |
40 |
41 |
--------------------------------------------------------------------------------
/DMHY/MASPreferencesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // Any controller providing preference pane view must support this protocol
3 | //
4 |
5 | #import
6 |
7 | @protocol MASPreferencesViewController
8 |
9 | @optional
10 |
11 | - (void)viewWillAppear;
12 | - (void)viewDidDisappear;
13 | - (NSView *)initialKeyView;
14 |
15 | @property (nonatomic, readonly) BOOL hasResizableWidth;
16 | @property (nonatomic, readonly) BOOL hasResizableHeight;
17 |
18 | @required
19 |
20 | @property (nonatomic, readonly) NSString *identifier;
21 | @property (nonatomic, readonly) NSImage *toolbarItemImage;
22 | @property (nonatomic, readonly) NSString *toolbarItemLabel;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/DMHY/MASPreferencesWindow.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1080
5 | 12E55
6 | 3084
7 | 1187.39
8 | 626.00
9 |
13 |
20 |
24 |
28 |
83 |
84 |
85 | YES
86 |
87 |
88 | window
89 |
90 |
91 |
92 | 3
93 |
94 |
95 |
96 | toolbar
97 |
98 |
99 |
100 | 23
101 |
102 |
103 |
104 | delegate
105 |
106 |
107 |
108 | 20
109 |
110 |
111 |
112 | delegate
113 |
114 |
115 |
116 | 22
117 |
118 |
119 |
120 |
121 | YES
122 |
123 | 0
124 |
125 |
126 |
127 |
128 |
129 | -2
130 |
131 |
132 | File's Owner
133 |
134 |
135 | -1
136 |
137 |
138 | First Responder
139 |
140 |
141 | -3
142 |
143 |
144 | Application
145 |
146 |
147 | 1
148 |
149 |
150 | YES
151 |
152 |
153 |
154 |
155 |
156 |
157 | 2
158 |
159 |
160 |
161 |
162 | 4
163 |
164 |
165 | YES
166 |
167 |
168 |
169 |
170 |
171 |
172 | YES
173 |
174 | YES
175 | -1.IBPluginDependency
176 | -2.IBPluginDependency
177 | -3.IBPluginDependency
178 | 1.IBNSWindowAutoPositionCentersHorizontal
179 | 1.IBNSWindowAutoPositionCentersVertical
180 | 1.IBPluginDependency
181 | 1.IBWindowTemplateEditedContentRect
182 | 1.NSWindowTemplate.visibleAtLaunch
183 | 2.IBPluginDependency
184 | 4.IBPluginDependency
185 |
186 |
187 | YES
188 | com.apple.InterfaceBuilder.CocoaPlugin
189 | com.apple.InterfaceBuilder.CocoaPlugin
190 | com.apple.InterfaceBuilder.CocoaPlugin
191 |
192 |
193 | com.apple.InterfaceBuilder.CocoaPlugin
194 | {{484, 402}, {360, 270}}
195 |
196 | com.apple.InterfaceBuilder.CocoaPlugin
197 | com.apple.InterfaceBuilder.CocoaPlugin
198 |
199 |
200 |
201 | YES
202 |
203 |
204 |
205 |
206 |
207 | YES
208 |
209 |
210 |
211 |
212 | 23
213 |
214 |
215 |
216 | YES
217 |
218 | MASPreferencesWindowController
219 | NSWindowController
220 |
221 | YES
222 |
223 | YES
224 | goNextTab:
225 | goPreviousTab:
226 |
227 |
228 | YES
229 | id
230 | id
231 |
232 |
233 |
234 | YES
235 |
236 | YES
237 | goNextTab:
238 | goPreviousTab:
239 |
240 |
241 | YES
242 |
243 | goNextTab:
244 | id
245 |
246 |
247 | goPreviousTab:
248 | id
249 |
250 |
251 |
252 |
253 | IBProjectSource
254 | ./Classes/MASPreferencesWindowController.h
255 |
256 |
257 |
258 |
259 | 0
260 | IBCocoaFramework
261 |
262 | com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
263 |
264 |
265 | YES
266 | 3
267 |
268 |
269 |
--------------------------------------------------------------------------------
/DMHY/MASPreferencesWindowController.h:
--------------------------------------------------------------------------------
1 | //
2 | // You create an application Preferences window using code like this:
3 | // _preferencesWindowController = [[MASPreferencesWindowController alloc] initWithViewControllers:controllers
4 | // title:title]
5 | //
6 | // To open the Preferences window:
7 | // [_preferencesWindowController showWindow:sender]
8 | //
9 |
10 | #import "MASPreferencesViewController.h"
11 |
12 | extern NSString *const kMASPreferencesWindowControllerDidChangeViewNotification;
13 |
14 | __attribute__((__visibility__("default")))
15 | #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
16 | @interface MASPreferencesWindowController : NSWindowController
17 | #else
18 | @interface MASPreferencesWindowController : NSWindowController
19 | #endif
20 | {
21 | @private
22 | NSMutableArray *_viewControllers;
23 | NSMutableDictionary *_minimumViewRects;
24 | NSString *_title;
25 | NSViewController *_selectedViewController;
26 | NSToolbar * __unsafe_unretained _toolbar;
27 | }
28 |
29 | @property (nonatomic, readonly) NSMutableArray *viewControllers;
30 | @property (nonatomic, readonly) NSUInteger indexOfSelectedController;
31 | @property (nonatomic, readonly, retain) NSViewController *selectedViewController;
32 | @property (nonatomic, readonly) NSString *title;
33 | @property (nonatomic, assign) IBOutlet NSToolbar *toolbar;
34 |
35 | - (id)initWithViewControllers:(NSArray *)viewControllers;
36 | - (id)initWithViewControllers:(NSArray *)viewControllers title:(NSString *)title;
37 | - (void)addViewController:(NSViewController *) viewController;
38 |
39 | - (void)selectControllerAtIndex:(NSUInteger)controllerIndex;
40 | - (void)selectControllerWithIdentifier:(NSString *)identifier;
41 |
42 | - (IBAction)goNextTab:(id)sender;
43 | - (IBAction)goPreviousTab:(id)sender;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/DMHY/MASPreferencesWindowController.m:
--------------------------------------------------------------------------------
1 | #import "MASPreferencesWindowController.h"
2 |
3 | NSString *const kMASPreferencesWindowControllerDidChangeViewNotification = @"MASPreferencesWindowControllerDidChangeViewNotification";
4 |
5 | static NSString *const kMASPreferencesFrameTopLeftKey = @"MASPreferences Frame Top Left";
6 | static NSString *const kMASPreferencesSelectedViewKey = @"MASPreferences Selected Identifier View";
7 |
8 | static NSString * PreferencesKeyForViewBounds (NSString *identifier)
9 | {
10 | return [NSString stringWithFormat:@"MASPreferences %@ Frame", identifier];
11 | }
12 |
13 | @interface MASPreferencesWindowController () // Private
14 |
15 | - (NSViewController *)viewControllerForIdentifier:(NSString *)identifier;
16 |
17 | @property (readonly) NSArray *toolbarItemIdentifiers;
18 | @property (nonatomic, retain) NSViewController *selectedViewController;
19 |
20 | @end
21 |
22 | #pragma mark -
23 |
24 | @implementation MASPreferencesWindowController
25 |
26 | @synthesize viewControllers = _viewControllers;
27 | @synthesize selectedViewController = _selectedViewController;
28 | @synthesize title = _title;
29 | @synthesize toolbar = _toolbar;
30 | #pragma mark -
31 |
32 | - (id)initWithViewControllers:(NSArray *)viewControllers
33 | {
34 | return [self initWithViewControllers:viewControllers title:nil];
35 | }
36 |
37 | - (id)initWithViewControllers:(NSArray *)viewControllers title:(NSString *)title
38 | {
39 | NSString *nibPath = [[NSBundle bundleForClass:MASPreferencesWindowController.class] pathForResource:@"MASPreferencesWindow" ofType:@"nib"];
40 | if ((self = [super initWithWindowNibPath:nibPath owner:self]))
41 | {
42 | _viewControllers = [NSMutableArray arrayWithArray: viewControllers];
43 | #if !__has_feature(objc_arc)
44 | _viewControllers = [_viewControllers retain];
45 | #endif
46 | _minimumViewRects = [[NSMutableDictionary alloc] init];
47 | _title = [title copy];
48 | }
49 | return self;
50 | }
51 |
52 | - (void)dealloc
53 | {
54 | [[NSNotificationCenter defaultCenter] removeObserver:self];
55 | [[self window] setDelegate:nil];
56 | for (NSToolbarItem *item in [self.toolbar items]) {
57 | item.target = nil;
58 | item.action = nil;
59 | }
60 | self.toolbar.delegate = nil;
61 | #if !__has_feature(objc_arc)
62 | [_viewControllers release];
63 | [_selectedViewController release];
64 | [_minimumViewRects release];
65 | [_title release];
66 | [super dealloc];
67 | #endif
68 | }
69 |
70 | - (void)addViewController: (NSViewController *) viewController
71 | {
72 | [_viewControllers addObject: viewController];
73 | [_toolbar insertItemWithItemIdentifier: [viewController identifier] atIndex: ([_viewControllers count] - 1)];
74 | [_toolbar validateVisibleItems];
75 | }
76 |
77 | #pragma mark -
78 |
79 | - (void)windowDidLoad
80 | {
81 | if ([self.title length] > 0)
82 | [[self window] setTitle:self.title];
83 |
84 | if ([self.viewControllers count])
85 | self.selectedViewController = [self viewControllerForIdentifier:[[NSUserDefaults standardUserDefaults] stringForKey:kMASPreferencesSelectedViewKey]] ?: [self firstViewController];
86 |
87 | NSString *origin = [[NSUserDefaults standardUserDefaults] stringForKey:kMASPreferencesFrameTopLeftKey];
88 | if (origin)
89 | [self.window setFrameTopLeftPoint:NSPointFromString(origin)];
90 |
91 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:self.window];
92 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:self.window];
93 | }
94 |
95 | - (NSViewController *)firstViewController {
96 | for (id viewController in self.viewControllers)
97 | if ([viewController isKindOfClass:[NSViewController class]])
98 | return viewController;
99 |
100 | return nil;
101 | }
102 |
103 | #pragma mark -
104 | #pragma mark NSWindowDelegate
105 |
106 | - (BOOL)windowShouldClose:(id __unused)sender
107 | {
108 | return !self.selectedViewController || [self.selectedViewController commitEditing];
109 | }
110 |
111 | - (void)windowDidMove:(NSNotification* __unused)aNotification
112 | {
113 | [[NSUserDefaults standardUserDefaults] setObject:NSStringFromPoint(NSMakePoint(NSMinX([self.window frame]), NSMaxY([self.window frame]))) forKey:kMASPreferencesFrameTopLeftKey];
114 | }
115 |
116 | - (void)windowDidResize:(NSNotification* __unused)aNotification
117 | {
118 | NSViewController *viewController = self.selectedViewController;
119 | if (viewController)
120 | [[NSUserDefaults standardUserDefaults] setObject:NSStringFromRect([viewController.view bounds]) forKey:PreferencesKeyForViewBounds(viewController.identifier)];
121 | }
122 |
123 | #pragma mark -
124 | #pragma mark Accessors
125 |
126 | - (NSArray *)toolbarItemIdentifiers
127 | {
128 | NSMutableArray *identifiers = [NSMutableArray arrayWithCapacity:_viewControllers.count];
129 | for (id viewController in _viewControllers)
130 | if (viewController == [NSNull null])
131 | [identifiers addObject:NSToolbarFlexibleSpaceItemIdentifier];
132 | else
133 | [identifiers addObject:[viewController identifier]];
134 | return identifiers;
135 | }
136 |
137 | #pragma mark -
138 |
139 | - (NSUInteger)indexOfSelectedController
140 | {
141 | NSUInteger index = [self.toolbarItemIdentifiers indexOfObject:self.selectedViewController.identifier];
142 | return index;
143 | }
144 |
145 | #pragma mark -
146 | #pragma mark NSToolbarDelegate
147 |
148 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar * __unused)toolbar
149 | {
150 | NSArray *identifiers = self.toolbarItemIdentifiers;
151 | return identifiers;
152 | }
153 |
154 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar * __unused)toolbar
155 | {
156 | NSArray *identifiers = self.toolbarItemIdentifiers;
157 | return identifiers;
158 | }
159 |
160 | - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar * __unused)toolbar
161 | {
162 | NSArray *identifiers = self.toolbarItemIdentifiers;
163 | return identifiers;
164 | }
165 |
166 | - (NSToolbarItem *)toolbar:(NSToolbar * __unused)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL __unused)flag
167 | {
168 | NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
169 | NSArray *identifiers = self.toolbarItemIdentifiers;
170 | NSUInteger controllerIndex = [identifiers indexOfObject:itemIdentifier];
171 | if (controllerIndex != NSNotFound)
172 | {
173 | id controller = [_viewControllers objectAtIndex:controllerIndex];
174 | toolbarItem.image = controller.toolbarItemImage;
175 | toolbarItem.label = controller.toolbarItemLabel;
176 | toolbarItem.target = self;
177 | toolbarItem.action = @selector(toolbarItemDidClick:);
178 | }
179 | #if !__has_feature(objc_arc)
180 | [toolbarItem autorelease];
181 | #endif
182 | return toolbarItem;
183 | }
184 |
185 | #pragma mark -
186 | #pragma mark Private methods
187 |
188 | - (NSViewController *)viewControllerForIdentifier:(NSString *)identifier
189 | {
190 | for (id viewController in self.viewControllers) {
191 | if (viewController == [NSNull null]) continue;
192 | if ([[viewController identifier] isEqualToString:identifier])
193 | return viewController;
194 | }
195 | return nil;
196 | }
197 |
198 | #pragma mark -
199 |
200 | - (void)setSelectedViewController:(NSViewController *)controller
201 | {
202 | if (_selectedViewController == controller)
203 | return;
204 |
205 | if (_selectedViewController)
206 | {
207 | // Check if we can commit changes for old controller
208 | if (![_selectedViewController commitEditing])
209 | {
210 | [[self.window toolbar] setSelectedItemIdentifier:_selectedViewController.identifier];
211 | return;
212 | }
213 |
214 | #if __has_feature(objc_arc)
215 | [self.window setContentView:[[NSView alloc] init]];
216 | #else
217 | [self.window setContentView:[[[NSView alloc] init] autorelease]];
218 | #endif
219 | [_selectedViewController setNextResponder:nil];
220 | if ([_selectedViewController respondsToSelector:@selector(viewDidDisappear)])
221 | [_selectedViewController viewDidDisappear];
222 |
223 | #if !__has_feature(objc_arc)
224 | [_selectedViewController release];
225 | #endif
226 | _selectedViewController = nil;
227 | }
228 |
229 | if (!controller)
230 | return;
231 |
232 | // Retrieve the new window tile from the controller view
233 | if ([self.title length] == 0)
234 | {
235 | NSString *label = controller.toolbarItemLabel;
236 | self.window.title = label;
237 | }
238 |
239 | [[self.window toolbar] setSelectedItemIdentifier:controller.identifier];
240 |
241 | // Record new selected controller in user defaults
242 | [[NSUserDefaults standardUserDefaults] setObject:controller.identifier forKey:kMASPreferencesSelectedViewKey];
243 |
244 | NSView *controllerView = controller.view;
245 |
246 | // Retrieve current and minimum frame size for the view
247 | NSString *oldViewRectString = [[NSUserDefaults standardUserDefaults] stringForKey:PreferencesKeyForViewBounds(controller.identifier)];
248 | NSString *minViewRectString = [_minimumViewRects objectForKey:controller.identifier];
249 | if (!minViewRectString)
250 | [_minimumViewRects setObject:NSStringFromRect(controllerView.bounds) forKey:controller.identifier];
251 |
252 | BOOL sizableWidth = ([controller respondsToSelector:@selector(hasResizableWidth)]
253 | ? controller.hasResizableWidth
254 | : controllerView.autoresizingMask & NSViewWidthSizable);
255 | BOOL sizableHeight = ([controller respondsToSelector:@selector(hasResizableHeight)]
256 | ? controller.hasResizableHeight
257 | : controllerView.autoresizingMask & NSViewHeightSizable);
258 |
259 | NSRect oldViewRect = oldViewRectString ? NSRectFromString(oldViewRectString) : controllerView.bounds;
260 | NSRect minViewRect = minViewRectString ? NSRectFromString(minViewRectString) : controllerView.bounds;
261 | oldViewRect.size.width = NSWidth(oldViewRect) < NSWidth(minViewRect) || !sizableWidth ? NSWidth(minViewRect) : NSWidth(oldViewRect);
262 | oldViewRect.size.height = NSHeight(oldViewRect) < NSHeight(minViewRect) || !sizableHeight ? NSHeight(minViewRect) : NSHeight(oldViewRect);
263 |
264 | [controllerView setFrame:oldViewRect];
265 |
266 | // Calculate new window size and position
267 | NSRect oldFrame = [self.window frame];
268 | NSRect newFrame = [self.window frameRectForContentRect:oldViewRect];
269 | newFrame = NSOffsetRect(newFrame, NSMinX(oldFrame), NSMaxY(oldFrame) - NSMaxY(newFrame));
270 |
271 | // Setup min/max sizes and show/hide resize indicator
272 | [self.window setContentMinSize:minViewRect.size];
273 | [self.window setContentMaxSize:NSMakeSize(sizableWidth ? CGFLOAT_MAX : NSWidth(oldViewRect), sizableHeight ? CGFLOAT_MAX : NSHeight(oldViewRect))];
274 | [self.window setShowsResizeIndicator:sizableWidth || sizableHeight];
275 | [[self.window standardWindowButton:NSWindowZoomButton] setEnabled:sizableWidth || sizableHeight];
276 |
277 | [self.window setFrame:newFrame display:YES animate:[self.window isVisible]];
278 |
279 | #if __has_feature(objc_arc)
280 | _selectedViewController = controller;
281 | #else
282 | _selectedViewController = [controller retain];
283 | #endif
284 | // In OSX 10.10, setContentView below calls viewWillAppear. We still want to call viewWillAppear on < 10.10,
285 | // so the check below avoids calling viewWillAppear twice on 10.10.
286 | // See https://github.com/shpakovski/MASPreferences/issues/32 for more info.
287 | if (![NSViewController instancesRespondToSelector:@selector(viewWillAppear)])
288 | if ([controller respondsToSelector:@selector(viewWillAppear)])
289 | [controller viewWillAppear];
290 |
291 | [self.window setContentView:controllerView];
292 | [self.window recalculateKeyViewLoop];
293 | if ([self.window firstResponder] == self.window) {
294 | if ([controller respondsToSelector:@selector(initialKeyView)])
295 | [self.window makeFirstResponder:[controller initialKeyView]];
296 | else
297 | [self.window selectKeyViewFollowingView:controllerView];
298 | }
299 |
300 | // Insert view controller into responder chain on 10.9 and earlier
301 | if (controllerView.nextResponder != controller) {
302 | controller.nextResponder = controllerView.nextResponder;
303 | controllerView.nextResponder = controller;
304 | }
305 |
306 | [[NSNotificationCenter defaultCenter] postNotificationName:kMASPreferencesWindowControllerDidChangeViewNotification object:self];
307 | }
308 |
309 | - (void)toolbarItemDidClick:(id)sender
310 | {
311 | if ([sender respondsToSelector:@selector(itemIdentifier)])
312 | self.selectedViewController = [self viewControllerForIdentifier:[sender itemIdentifier]];
313 | }
314 |
315 | #pragma mark -
316 | #pragma mark Public methods
317 |
318 | - (void)selectControllerAtIndex:(NSUInteger)controllerIndex
319 | {
320 | if (NSLocationInRange(controllerIndex, NSMakeRange(0, _viewControllers.count)))
321 | self.selectedViewController = [self.viewControllers objectAtIndex:controllerIndex];
322 | }
323 |
324 | - (void)selectControllerWithIdentifier:(NSString *)identifier
325 | {
326 | self.selectedViewController = [self viewControllerForIdentifier:identifier];
327 | }
328 |
329 | #pragma mark -
330 | #pragma mark Actions
331 |
332 | - (IBAction)goNextTab:(id __unused)sender
333 | {
334 | NSUInteger selectedIndex = self.indexOfSelectedController;
335 | NSUInteger numberOfControllers = [_viewControllers count];
336 |
337 | do { selectedIndex = (selectedIndex + 1) % numberOfControllers; }
338 | while ([_viewControllers objectAtIndex:selectedIndex] == [NSNull null]);
339 |
340 | [self selectControllerAtIndex:selectedIndex];
341 | }
342 |
343 | - (IBAction)goPreviousTab:(id __unused)sender
344 | {
345 | NSUInteger selectedIndex = self.indexOfSelectedController;
346 | NSUInteger numberOfControllers = [_viewControllers count];
347 |
348 | do { selectedIndex = (selectedIndex + numberOfControllers - 1) % numberOfControllers; }
349 | while ([_viewControllers objectAtIndex:selectedIndex] == [NSNull null]);
350 |
351 | [self selectControllerAtIndex:selectedIndex];
352 | }
353 |
354 | @end
355 |
--------------------------------------------------------------------------------
/DMHY/MainSplitViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MainSplitViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/19/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MainSplitViewController : NSSplitViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/MainSplitViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MainSplitViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/19/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "MainSplitViewController.h"
10 | #import "SideViewController.h"
11 |
12 | @interface MainSplitViewController ()
13 |
14 | @property (nonatomic, assign) BOOL isShowKeywordManager;
15 |
16 | @end
17 |
18 | @implementation MainSplitViewController
19 |
20 | - (void)viewDidLoad {
21 |
22 | [super viewDidLoad];
23 | [self setupMenuItems];
24 | }
25 | - (void)setupMenuItems {
26 | NSMenu *viewSubMenu = [self viewSubMenu];
27 | NSMenuItem *toggleKeywordManagerMenuItem = [[NSMenuItem alloc] initWithTitle:@"隐藏关键字管理区域"
28 | action:@selector(toggleKeywordManager:)
29 | keyEquivalent:@""];
30 | self.isShowKeywordManager = NO;
31 | [viewSubMenu addItem:toggleKeywordManagerMenuItem];
32 | }
33 |
34 | - (void)toggleKeywordManager:(id) sender {
35 | NSSplitViewItem *item = self.splitViewItems[0];
36 | item.collapsed = !item.collapsed;
37 | self.isShowKeywordManager = !self.isShowKeywordManager;
38 | NSMenu *viewSubMenu = [self viewSubMenu];
39 | NSMenuItem *toggleKeywordManagerMenuItem = [viewSubMenu itemAtIndex:3];
40 | toggleKeywordManagerMenuItem.title = self.isShowKeywordManager ? @"显示关键字管理区域" : @"隐藏关键字管理区域";
41 | }
42 |
43 | - (NSMenu *)viewSubMenu {
44 | NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
45 | NSMenuItem *viewMenuItem = [mainMenu itemWithTitle:@"View"];
46 | return [viewMenuItem submenu];
47 | }
48 |
49 | /*
50 | Always show divider.
51 | */
52 | - (BOOL)splitView:(NSSplitView *)splitView shouldHideDividerAtIndex:(NSInteger)dividerIndex {
53 | [super splitView:splitView shouldHideDividerAtIndex:dividerIndex];
54 | return NO;
55 | }
56 |
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/DMHY/Model/Bangumi.h:
--------------------------------------------------------------------------------
1 | //
2 | // Bangumi.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/20/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface Bangumi : NSObject
12 |
13 | @property (nonatomic, strong) NSString *titleCN;
14 | @property (nonatomic, strong) NSString *titleCNFull;
15 | @property (nonatomic, strong) NSString *titleJP;
16 | @property (nonatomic, strong) NSString *titleEN;
17 | @property (nonatomic, strong) NSString *weekDayJP;
18 | @property (nonatomic, strong) NSString *weekDayCN;
19 | @property (nonatomic, strong) NSString *timeJP;
20 | @property (nonatomic, strong) NSString *timeCN;
21 | @property (nonatomic, strong) NSString *showDate;
22 | @property (nonatomic, strong) NSString *officalSite;
23 | @property (nonatomic, strong) NSString *subGroup;
24 |
25 | @property (nonatomic, assign, getter=isNewBgm) BOOL newBgm;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/DMHY/Model/Bangumi.m:
--------------------------------------------------------------------------------
1 | //
2 | // Bangumi.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/20/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "Bangumi.h"
10 |
11 | @implementation Bangumi
12 |
13 | - (NSString *)description {
14 | return [NSString stringWithFormat:@"Bangumi title %@ weekDayCN %@ timeCN %@ showDate %@", self.titleCN, self.weekDayCN, self.timeCN, self.showDate];
15 | }
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYKeyword+CoreDataProperties.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYKeyword+CoreDataProperties.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYKeyword.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface DMHYKeyword (CoreDataProperties)
17 |
18 | @property (nullable, nonatomic, retain) NSDate *createDate;
19 | @property (nullable, nonatomic, retain) NSNumber *isSubKeyword;
20 | @property (nullable, nonatomic, retain) NSString *keyword;
21 | @property (nullable, nonatomic, retain) NSSet *subKeywords;
22 | @property (nullable, nonatomic, retain) NSSet *torrents;
23 | @property (nullable, nonatomic, retain) DMHYSite *site;
24 |
25 | @end
26 |
27 | @interface DMHYKeyword (CoreDataGeneratedAccessors)
28 |
29 | - (void)addSubKeywordsObject:(DMHYKeyword *)value;
30 | - (void)removeSubKeywordsObject:(DMHYKeyword *)value;
31 | - (void)addSubKeywords:(NSSet *)values;
32 | - (void)removeSubKeywords:(NSSet *)values;
33 |
34 | - (void)addTorrentsObject:(DMHYTorrent *)value;
35 | - (void)removeTorrentsObject:(DMHYTorrent *)value;
36 | - (void)addTorrents:(NSSet *)values;
37 | - (void)removeTorrents:(NSSet *)values;
38 |
39 | @end
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYKeyword+CoreDataProperties.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYKeyword+CoreDataProperties.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYKeyword+CoreDataProperties.h"
13 |
14 | @implementation DMHYKeyword (CoreDataProperties)
15 |
16 | @dynamic createDate;
17 | @dynamic isSubKeyword;
18 | @dynamic keyword;
19 | @dynamic subKeywords;
20 | @dynamic torrents;
21 | @dynamic site;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYKeyword.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYKeyword.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/23/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class DMHYTorrent;
13 | @class DMHYSite;
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface DMHYKeyword : NSManagedObject
18 |
19 | // Insert code here to declare functionality of your managed object subclass
20 |
21 | + (DMHYKeyword *)entityForKeywordName:(NSString *)name isSubKeyword:(NSNumber *)subKeyword inManagedObjectContext:(NSManagedObjectContext *)context;
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
27 | #import "DMHYKeyword+CoreDataProperties.h"
28 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYKeyword.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYKeyword.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/23/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYKeyword.h"
10 | #import "DMHYTorrent.h"
11 | #import "DMHYSite.h"
12 |
13 | @implementation DMHYKeyword
14 |
15 | // Insert code here to add functionality to your managed object subclass
16 |
17 | + (DMHYKeyword *)entityForKeywordName:(NSString *)name isSubKeyword:(NSNumber *)subKeyword inManagedObjectContext:(NSManagedObjectContext *)context {
18 | DMHYKeyword *entity = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:context];
19 | entity.keyword = name;
20 | entity.createDate = [NSDate new];
21 | entity.isSubKeyword = subKeyword;
22 | return entity;
23 | }
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYSite+CoreDataProperties.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSite+CoreDataProperties.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYSite.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface DMHYSite (CoreDataProperties)
17 |
18 | @property (nullable, nonatomic, retain) NSString *name;
19 | @property (nullable, nonatomic, retain) NSString *mainURL;
20 | @property (nullable, nonatomic, retain) NSString *searchURL;
21 | @property (nullable, nonatomic, retain) NSNumber *isAutoDownload;
22 | @property (nullable, nonatomic, retain) NSString *downloadType;
23 | @property (nullable, nonatomic, retain) NSNumber *isDownloadFin;
24 | @property (nullable, nonatomic, retain) NSNumber *isCurrentUse;
25 | @property (nullable, nonatomic, retain) NSNumber *isFliterSite;
26 | @property (nullable, nonatomic, retain) NSDate *createDate;
27 | @property (nullable, nonatomic, retain) NSString *responseType;
28 | @property (nullable, nonatomic, retain) NSSet *keywords;
29 |
30 | @end
31 |
32 | @interface DMHYSite (CoreDataGeneratedAccessors)
33 |
34 | - (void)addKeywordsObject:(DMHYKeyword *)value;
35 | - (void)removeKeywordsObject:(DMHYKeyword *)value;
36 | - (void)addKeywords:(NSSet *)values;
37 | - (void)removeKeywords:(NSSet *)values;
38 |
39 | @end
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYSite+CoreDataProperties.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSite+CoreDataProperties.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYSite+CoreDataProperties.h"
13 |
14 | @implementation DMHYSite (CoreDataProperties)
15 |
16 | @dynamic name;
17 | @dynamic mainURL;
18 | @dynamic searchURL;
19 | @dynamic isAutoDownload;
20 | @dynamic downloadType;
21 | @dynamic isDownloadFin;
22 | @dynamic isCurrentUse;
23 | @dynamic isFliterSite;
24 | @dynamic createDate;
25 | @dynamic responseType;
26 | @dynamic keywords;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYSite.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSite.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class DMHYKeyword;
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface DMHYSite : NSManagedObject
17 |
18 | // Insert code here to declare functionality of your managed object subclass
19 |
20 | + (DMHYSite *)entityFormDictionary:(NSDictionary *)site inManagedObjectContext:(NSManagedObjectContext *)context;
21 |
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
26 | #import "DMHYSite+CoreDataProperties.h"
27 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYSite.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYSite.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/16/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYSite.h"
10 | #import "DMHYKeyword.h"
11 | #import "DMHYAPI.h"
12 |
13 | @implementation DMHYSite
14 |
15 | // Insert code here to add functionality to your managed object subclass
16 |
17 | + (DMHYSite *)entityFormDictionary:(NSDictionary *)site inManagedObjectContext:(NSManagedObjectContext *)context {
18 | DMHYSite *siteEntity = [NSEntityDescription insertNewObjectForEntityForName:DMHYSiteEntityKey inManagedObjectContext:context];
19 | siteEntity.name = site[DMHYSiteNameKey];
20 | siteEntity.mainURL = site[DMHYSiteMainURLKey];
21 | siteEntity.searchURL = site[DMHYSiteSearchURLKey];
22 | siteEntity.isFliterSite = site[DMHYSiteFliterKey];
23 | siteEntity.isAutoDownload = site[DMHYSiteAutoDLKey];
24 | siteEntity.downloadType = site[DMHYSiteDLTypeKey];
25 | siteEntity.isDownloadFin = site[DMHYSiteDLFinKey];
26 | siteEntity.responseType = site[DMHYSiteResponseTypeKey];
27 | siteEntity.isCurrentUse = site[DMHYSiteCurrentUseKey];
28 | siteEntity.createDate = [NSDate new];
29 | return siteEntity;
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYTorrent+CoreDataProperties.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTorrent+CoreDataProperties.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/2/28.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYTorrent.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface DMHYTorrent (CoreDataProperties)
17 |
18 | @property (nullable, nonatomic, retain) NSString *author;
19 | @property (nullable, nonatomic, retain) NSString *category;
20 | @property (nullable, nonatomic, retain) NSNumber *isDownloaded;
21 | @property (nullable, nonatomic, retain) NSNumber *isNewTorrent;
22 | @property (nullable, nonatomic, retain) NSString *link;
23 | @property (nullable, nonatomic, retain) NSString *magnet;
24 | @property (nullable, nonatomic, retain) NSDate *pubDate;
25 | @property (nullable, nonatomic, retain) NSString *title;
26 | @property (nullable, nonatomic, retain) DMHYKeyword *keyword;
27 |
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYTorrent+CoreDataProperties.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTorrent+CoreDataProperties.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/2/28.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
9 | // to delete and recreate this implementation file for your updated model.
10 | //
11 |
12 | #import "DMHYTorrent+CoreDataProperties.h"
13 |
14 | @implementation DMHYTorrent (CoreDataProperties)
15 |
16 | @dynamic author;
17 | @dynamic category;
18 | @dynamic isDownloaded;
19 | @dynamic isNewTorrent;
20 | @dynamic link;
21 | @dynamic magnet;
22 | @dynamic pubDate;
23 | @dynamic title;
24 | @dynamic keyword;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYTorrent.h:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTorrent.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/23/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class DMHYKeyword;
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface DMHYTorrent : NSManagedObject
17 |
18 | // Insert code here to declare functionality of your managed object subclass
19 |
20 | @end
21 |
22 | NS_ASSUME_NONNULL_END
23 |
24 | #import "DMHYTorrent+CoreDataProperties.h"
25 |
--------------------------------------------------------------------------------
/DMHY/Model/DMHYTorrent.m:
--------------------------------------------------------------------------------
1 | //
2 | // DMHYTorrent.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/23/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "DMHYTorrent.h"
10 | #import "DMHYKeyword.h"
11 |
12 | @implementation DMHYTorrent
13 |
14 | // Insert code here to add functionality to your managed object subclass
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/DMHY/Model/FileItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // FileItem.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/26/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface FileItem : NSObject
12 |
13 | @property (nonatomic, strong) NSURL *url;
14 | @property (nonatomic, strong) NSString *fileName;
15 | @property (nonatomic, strong) NSDate *modifyDate;
16 |
17 | - (instancetype )initWithURL:(NSURL *)url fileName:(NSString *)name modifyDate:(NSDate *)date;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/DMHY/Model/FileItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // FileItem.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/26/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "FileItem.h"
10 |
11 | @implementation FileItem
12 |
13 | - (instancetype)initWithURL:(NSURL *)url fileName:(NSString *)name modifyDate:(NSDate *)date {
14 | self = [super init];
15 | if (self) {
16 | _url = url;
17 | _fileName = name;
18 | _modifyDate = date;
19 | }
20 | return self;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/DMHY/NavigationView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationView.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/9/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NavigationView : NSView
12 | @end
13 |
--------------------------------------------------------------------------------
/DMHY/NavigationView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationView.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/9/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "NavigationView.h"
10 | #import "PreferenceController.h"
11 |
12 | #define DMHYThemeKey @"ThemeType"
13 |
14 | typedef NS_ENUM(NSInteger, DMHYThemeType) {
15 | DMHYThemeLight,
16 | DMHYThemeDark
17 | };
18 |
19 | @implementation NavigationView
20 |
21 | - (void)drawRect:(NSRect)dirtyRect {
22 | [super drawRect:dirtyRect];
23 |
24 | // Drawing code here.
25 |
26 | NSInteger themecode = [PreferenceController preferenceTheme];
27 | switch (themecode) {
28 | case DMHYThemeLight:
29 | self.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight];
30 | break;
31 | case DMHYThemeDark:
32 | self.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark];
33 | default:
34 | break;
35 | }
36 | }
37 |
38 | - (BOOL)allowsVibrancy {
39 | return YES;
40 | }
41 |
42 | - (void)viewWillDraw {
43 |
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/DMHY/PreferenceController.h:
--------------------------------------------------------------------------------
1 | //
2 | // PreferenceController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/16/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MASPreferencesViewController.h"
11 |
12 | extern NSString * const FliterKeywordKey;
13 | extern NSString * const DontDownloadCollectionKey;
14 | extern NSString * const DMHYDontDownloadCollectionKeyDidChangedNotification;
15 |
16 | @interface PreferenceController : NSViewController
17 |
18 | + (void)setPreferenceDownloadLinkType:(BOOL)type;
19 | + (BOOL)preferenceDownloadLinkType;
20 |
21 | + (void)setPreferenceSavePath:(NSURL *)path;
22 | + (NSURL *)preferenceSavePath;
23 |
24 | + (void)setFileWatchPath:(NSURL *)path;
25 | + (NSURL *)fileWatchPath;
26 |
27 | + (void)setPreferenceFetchInterval:(NSInteger) seconds;
28 | + (NSInteger)preferenceFetchInterval;
29 |
30 | + (void)setPreferenceTheme:(NSInteger) themeCode;
31 | + (NSInteger)preferenceTheme;
32 |
33 | + (void)setPreferenceDontDownloadCollection:(BOOL)value;
34 | + (BOOL)preferenceDontDownloadCollection;
35 |
36 | + (NSURL *)userDownloadPath;
37 |
38 | + (void)setViewPreferenceTableViewRowStyle:(NSInteger)style;
39 | + (NSInteger)viewPreferenceTableViewRowStyle;
40 | + (void)setPreferenceDoubleAction:(NSInteger)action;
41 | + (NSInteger)preferenceDoubleAction;
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/DMHY/PreferenceController.m:
--------------------------------------------------------------------------------
1 | //
2 | // PreferenceController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/16/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "PreferenceController.h"
10 | #import "SitePreferenceController.h"
11 | #import "DMHYAPI.h"
12 | #import "DMHYCoreDataStackManager.h"
13 | #include
14 |
15 | NSString * const FliterKeywordKey = @"FliterKeyword";
16 | NSString * const DontDownloadCollectionKey = @"DontDownloadCollection";
17 | NSString * const DMHYDontDownloadCollectionKeyDidChangedNotification = @"DMHYDontDownloadCollectionKeyDidChangedNotification";
18 |
19 | @interface PreferenceController ()
20 | @property (weak) IBOutlet NSMatrix *downloadLinkTypeMatrix;
21 | @property (weak) IBOutlet NSTextField *savePathLabel;
22 | @property (weak) IBOutlet NSTextField *fileWatchPathLabel;
23 |
24 | @property (weak) IBOutlet NSPopUpButton *fetchIntervalPopUpButton;
25 |
26 | @property (weak) IBOutlet NSTextField *fliterKeywordTextField;
27 |
28 | @property (weak) IBOutlet NSButton *dontDownloadCollectionButton;
29 |
30 | @property (weak) IBOutlet NSMatrix *mainViewRowStyleMatrix;
31 | @property (weak) IBOutlet NSMatrix *doubleActionMatrix;
32 |
33 | @end
34 |
35 | @implementation PreferenceController
36 |
37 | - (instancetype)init {
38 | return [super initWithNibName:@"Preference" bundle:nil];
39 | }
40 |
41 | - (NSString *)identifier
42 | {
43 | return @"GeneralPreferences";
44 | }
45 |
46 | - (NSImage *)toolbarItemImage
47 | {
48 | return [NSImage imageNamed:NSImageNamePreferencesGeneral];
49 | }
50 |
51 | - (NSString *)toolbarItemLabel
52 | {
53 | return @"常用";
54 | }
55 |
56 |
57 |
58 | - (void)viewDidLoad {
59 | [super viewDidLoad];
60 | [self configurePreference];
61 | }
62 |
63 | - (void)configurePreference {
64 | NSInteger idx = [PreferenceController preferenceDownloadLinkType];
65 | [self.downloadLinkTypeMatrix selectCellAtRow:idx column:0];
66 |
67 | NSURL *url = [PreferenceController preferenceSavePath];
68 | if (url) {
69 | self.savePathLabel.stringValue = [url path];
70 | } else {
71 | self.savePathLabel.stringValue = [[PreferenceController userDownloadPath] path];
72 | }
73 |
74 | NSURL *fileWatchPath = [PreferenceController fileWatchPath];
75 | if (fileWatchPath) {
76 | self.fileWatchPathLabel.stringValue = fileWatchPath.path;
77 | } else {
78 | self.fileWatchPathLabel.stringValue = @"请设置文件查看路径 <_<";
79 | }
80 |
81 | NSInteger seconds = [PreferenceController preferenceFetchInterval];
82 | NSInteger minutes = seconds / 60;
83 |
84 | [self.fetchIntervalPopUpButton selectItemWithTitle:[NSString stringWithFormat:@"%li",(long)minutes]];
85 |
86 | NSInteger dontDownloadCollection = [PreferenceController preferenceDontDownloadCollection];
87 | self.dontDownloadCollectionButton.state = dontDownloadCollection;
88 |
89 | NSString *fliter = [[NSUserDefaults standardUserDefaults] stringForKey:FliterKeywordKey];
90 | self.fliterKeywordTextField.stringValue = fliter;
91 |
92 | NSInteger rowStyleIdx = [PreferenceController viewPreferenceTableViewRowStyle];
93 | [self.mainViewRowStyleMatrix selectCellAtRow:rowStyleIdx column:0];
94 | NSInteger rowDoubleAction = [PreferenceController preferenceDoubleAction];
95 | [self.doubleActionMatrix selectCellAtRow:rowDoubleAction column:0];
96 | }
97 |
98 | - (void)controlTextDidEndEditing:(NSNotification *)obj {
99 | NSString *fliter = ((NSTextField *)obj.object).stringValue;
100 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
101 | [userDefaults setObject:fliter forKey:FliterKeywordKey];
102 | [userDefaults synchronize];
103 | }
104 |
105 | - (IBAction)downloadLinkTypeChanged:(id)sender {
106 | //这里仅有 0 1 这两中,直接偷懒(毕竟 YES = 1 NO = 0)
107 | NSInteger linkType = [self.downloadLinkTypeMatrix selectedRow];
108 | [PreferenceController setPreferenceDownloadLinkType:linkType];
109 | [DMHYNotification postNotificationName:DMHYDownloadLinkTypeNotification];
110 | }
111 |
112 | - (IBAction)changeSavePath:(id)sender {
113 | NSOpenPanel *openPanel = [NSOpenPanel openPanel];
114 | openPanel.canChooseFiles = NO;
115 | openPanel.canChooseDirectories = YES;
116 | openPanel.prompt = @"OK";
117 | if ([openPanel runModal] == NSModalResponseOK) {
118 | [PreferenceController setPreferenceSavePath:[openPanel URL]];
119 | self.savePathLabel.stringValue = [[openPanel URL] path];
120 | }
121 | [DMHYNotification postNotificationName:DMHYSavePathChangedNotification];
122 | }
123 |
124 | - (IBAction)changeFileWatchPath:(id)sender {
125 | NSOpenPanel *openPanel = [NSOpenPanel openPanel];
126 | openPanel.canChooseFiles = NO;
127 | openPanel.canChooseDirectories = YES;
128 | openPanel.prompt = @"OK";
129 | if ([openPanel runModal] == NSModalResponseOK) {
130 | NSURL *url = [openPanel URL];
131 | [PreferenceController setFileWatchPath:url];
132 | self.fileWatchPathLabel.stringValue = [[openPanel URL] path];
133 | }
134 | [DMHYNotification postNotificationName:DMHYFileWatchPathChangedNotification];
135 | }
136 |
137 | - (IBAction)changeFetchInterval:(id)sender {
138 | NSInteger minitues = [((NSPopUpButton *)sender).titleOfSelectedItem integerValue];
139 | NSInteger seconds = minitues * 60;
140 | [PreferenceController setPreferenceFetchInterval:seconds];
141 | [DMHYNotification postNotificationName:DMHYFetchIntervalChangedNotification];
142 | }
143 |
144 | - (IBAction)dontDownloadCollection:(id)sender {
145 | NSInteger state = ((NSButton *)sender).state;
146 | [PreferenceController setPreferenceDontDownloadCollection:state];
147 | [DMHYNotification postNotificationName:DMHYDontDownloadCollectionKeyDidChangedNotification];
148 | }
149 |
150 | + (NSURL *)userDownloadPath {
151 | NSURL *downloadDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDownloadsDirectory
152 | inDomain:NSUserDomainMask
153 | appropriateForURL:nil
154 | create:NO
155 | error:nil];
156 | return downloadDirectoryURL;
157 | }
158 |
159 | + (void)setPreferenceDownloadLinkType:(BOOL)type {
160 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
161 | [userDefaults setBool:type forKey:kDownloadLinkType];
162 | [userDefaults synchronize];
163 | }
164 |
165 | + (BOOL)preferenceDownloadLinkType {
166 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
167 | return [userDefaults boolForKey:kDownloadLinkType];
168 | }
169 |
170 | + (void)setPreferenceDontDownloadCollection:(BOOL)value {
171 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
172 | [userDefaults setBool:value forKey:DontDownloadCollectionKey];
173 | [userDefaults synchronize];
174 | }
175 |
176 | + (BOOL)preferenceDontDownloadCollection {
177 | return [[NSUserDefaults standardUserDefaults] boolForKey:DontDownloadCollectionKey];
178 | }
179 |
180 | + (void)setPreferenceSavePath:(NSURL *)path {
181 | NSUserDefaults *userDefautls = [NSUserDefaults standardUserDefaults];
182 | [userDefautls setURL:path forKey:kSavePath];
183 | [userDefautls synchronize];
184 | }
185 |
186 | + (NSURL *)preferenceSavePath {
187 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
188 | return [userDefaults URLForKey:kSavePath];
189 | }
190 |
191 | + (void)setFileWatchPath:(NSURL *)url {
192 | NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:nil];
193 | NSUserDefaults *userDefautls = [NSUserDefaults standardUserDefaults];
194 | [userDefautls setObject:data forKey:kFileWatchPath];
195 | [userDefautls synchronize];
196 | [DMHYNotification postNotificationName:DMHYFileWatchPathChangedNotification];
197 | }
198 |
199 | + (NSURL *)fileWatchPath {
200 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
201 | NSData *filePathData = [userDefaults objectForKey:kFileWatchPath];
202 | BOOL bookmarkISStable = NO;
203 | NSURL *fileWatchPath = [NSURL URLByResolvingBookmarkData:filePathData options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&bookmarkISStable error:nil];
204 | return fileWatchPath;
205 | }
206 |
207 | + (void)setPreferenceFetchInterval:(NSInteger)seconds {
208 | NSUserDefaults *userDefautls = [NSUserDefaults standardUserDefaults];
209 | [userDefautls setInteger:seconds forKey:kFetchInterval];
210 | [userDefautls synchronize];
211 | }
212 |
213 | + (NSInteger)preferenceFetchInterval {
214 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
215 | return [userDefaults integerForKey:kFetchInterval];
216 | }
217 |
218 | + (void)setPreferenceTheme:(NSInteger)themeCode {
219 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
220 | switch (themeCode) {
221 | case DMHYThemeLight:
222 | [userDefaults setInteger:0 forKey:DMHYThemeKey];
223 | break;
224 | case DMHYThemeDark:
225 | [userDefaults setInteger:1 forKey:DMHYThemeKey];
226 | break;
227 | default:
228 | [userDefaults setInteger:0 forKey:DMHYThemeKey];
229 | break;
230 | }
231 | [userDefaults synchronize];
232 | }
233 |
234 | + (NSInteger)preferenceTheme {
235 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
236 | return [userDefaults integerForKey:DMHYThemeKey];
237 | }
238 |
239 | + (void)setViewPreferenceTableViewRowStyle:(NSInteger)style {
240 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
241 | [userDefaults setInteger:style forKey:kMainTableViewRowStyle];
242 | [userDefaults synchronize];
243 | }
244 |
245 | + (NSInteger)viewPreferenceTableViewRowStyle {
246 | return [[NSUserDefaults standardUserDefaults] integerForKey:kMainTableViewRowStyle];
247 | }
248 |
249 | + (void)setPreferenceDoubleAction:(NSInteger)action {
250 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
251 | [userDefaults setInteger:action forKey:kDoubleAction];
252 | [userDefaults synchronize];
253 | }
254 |
255 | + (NSInteger)preferenceDoubleAction {
256 | return [[NSUserDefaults standardUserDefaults] integerForKey:kDoubleAction];
257 | }
258 |
259 | - (IBAction)mainTableViewRowStyleChanged:(id)sender {
260 | NSInteger style = [self.mainViewRowStyleMatrix selectedRow];
261 | [PreferenceController setViewPreferenceTableViewRowStyle:style];
262 | [DMHYNotification postNotificationName:DMHYMainTableViewRowStyleChangedNotification];
263 | }
264 |
265 | - (IBAction)doubleActionChanged:(id)sender {
266 | NSInteger action = [self.doubleActionMatrix selectedRow];
267 | [PreferenceController setPreferenceDoubleAction:action];
268 | [DMHYNotification postNotificationName:DMHYDoubleActionChangedNotification];
269 | }
270 |
271 | - (IBAction)resetPreference:(id)sender {
272 | NSAlert *alert = [[NSAlert alloc] init];
273 | [alert setMessageText:@"确定要重置设置?"];
274 | [alert setInformativeText:@"该操作会删除所有自定义设置。重置设置成功之后,会重新启动本应用 _(:3 」∠)_"];
275 | [alert addButtonWithTitle:@"重置"];
276 | [alert addButtonWithTitle:@"取消"];
277 | [alert setAlertStyle:NSWarningAlertStyle];
278 |
279 | [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) {
280 | if (returnCode == NSAlertFirstButtonReturn) {
281 | NSString *domainName = [[NSBundle mainBundle] bundleIdentifier];
282 | [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:domainName];
283 | [[NSUserDefaults standardUserDefaults] synchronize];
284 | [self relaunchAfterDelay:1.0];
285 | }
286 | }];
287 |
288 | }
289 |
290 | - (void)relaunchAfterDelay:(float)seconds {
291 | NSTask *task = [[NSTask alloc] init];
292 | NSMutableArray *args = [NSMutableArray array];
293 | [args addObject:@"-c"];
294 | [args addObject:[NSString stringWithFormat:@"sleep %f; open \"%@\"", seconds, [[NSBundle mainBundle] bundlePath]]];
295 | [task setLaunchPath:@"/bin/sh"];
296 | [task setArguments:args];
297 | [task launch];
298 | [NSApp terminate:self];
299 | }
300 |
301 | - (IBAction)resetDatabase:(id)sender {
302 | NSAlert *alert = [[NSAlert alloc] init];
303 | [alert setMessageText:@"确定要重置数据库?"];
304 | [alert setInformativeText:@"该操作会删除所有已保存的关键字和已下载的种子记录。重置数据库成功之后,会自动重新启动本应用 _(:3 」∠)_"];
305 | [alert addButtonWithTitle:@"重置"];
306 | [alert addButtonWithTitle:@"取消"];
307 | [alert setAlertStyle:NSWarningAlertStyle];
308 |
309 | [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) {
310 | if (returnCode == NSAlertFirstButtonReturn) {
311 | if ([[DMHYCoreDataStackManager sharedManager] resetDatabase]) {
312 | [self relaunchAfterDelay:1.0];
313 | }
314 | }
315 | }];
316 |
317 | }
318 |
319 | @end
320 |
--------------------------------------------------------------------------------
/DMHY/Preferences/SitePreferenceController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SitePreferenceController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/22/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MASPreferencesViewController.h"
11 |
12 | extern NSString * const SiteResponseJSON;
13 | extern NSString * const SiteResponseXML;
14 |
15 | @interface SitePreferenceController : NSViewController
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/DMHY/Preferences/SitePreferenceController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SitePreferenceController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/22/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "SitePreferenceController.h"
10 | #import "DMHYCoreDataStackManager.h"
11 | #import "ButtonTableCellView.h"
12 |
13 | NSString * const SiteResponseJSON = @"JSON";
14 | NSString * const SiteResponseXML = @"XML";
15 |
16 | @interface SitePreferenceController ()
17 |
18 | @property (weak) IBOutlet NSTableView *tableView;
19 |
20 | @property (nonatomic, strong) NSArray *sites;
21 |
22 | @end
23 |
24 | @implementation SitePreferenceController
25 |
26 | - (void)viewDidLoad {
27 | [super viewDidLoad];
28 | // Do view setup here.
29 | [self observeNotifications];
30 | [self.tableView reloadData];
31 | }
32 |
33 | - (void)observeNotifications {
34 | [DMHYNotification addObserver:self selector:@selector(handleSeedDataCompleted) name:DMHYSeedDataCompletedNotification];
35 | }
36 |
37 | - (void)handleSeedDataCompleted {
38 | self.sites = nil;
39 | [self.tableView reloadData];
40 | }
41 |
42 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
43 | return self.sites.count;
44 | }
45 |
46 | - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
47 | return 22.0f;
48 | }
49 |
50 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
51 | NSString *identifier = tableColumn.identifier;
52 | DMHYSite *site = self.sites[row];
53 |
54 | if ([identifier isEqualToString:@"currentUseCell"]) {
55 | ButtonTableCellView *cellView = [tableView makeViewWithIdentifier:@"currentUseCell" owner:self];
56 | [cellView.checkButton setState:site.isCurrentUse.integerValue];
57 | return cellView;
58 | }
59 | if ([identifier isEqualToString:@"autoDownloadCell"]) {
60 | ButtonTableCellView *cellView = [tableView makeViewWithIdentifier:@"autoDownloadCell" owner:self];
61 | [cellView.checkButton setState:site.isAutoDownload.integerValue];
62 | return cellView;
63 | }
64 | if ([identifier isEqualToString:@"siteNameCell"]) {
65 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"siteNameCell" owner:self];
66 | cellView.textField.stringValue = site.name;
67 | return cellView;
68 | }
69 |
70 | if ([identifier isEqualToString:@"responseTypeCell"]) {
71 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"responseTypeCell" owner:self];
72 | cellView.textField.stringValue = site.responseType;
73 | return cellView;
74 | }
75 | if ([identifier isEqualToString:@"siteMainCell"]) {
76 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"siteMainCell" owner:self];
77 | cellView.textField.stringValue = site.mainURL;
78 | return cellView;
79 | }
80 | return nil;
81 | }
82 |
83 | - (IBAction)changeAutoDownload:(NSButton *)sender {
84 | NSInteger state = sender.state;
85 | NSInteger row = [self.tableView rowForView:sender];
86 | if (state < 0 || state > 1 || row < 0) {
87 | return;
88 | }
89 | DMHYSite *site = self.sites[row];
90 | site.isAutoDownload = @(state);
91 | [[DMHYCoreDataStackManager sharedManager] saveContext];
92 | [DMHYNotification postNotificationName:DMHYAutoDownloadSiteChangedNotification];
93 | }
94 |
95 | - (IBAction)changeCurrentUse:(NSButton *)sender {
96 | NSInteger state = sender.state;
97 | NSInteger row = [self.tableView rowForView:sender];
98 | if (state < 0 || state > 1 || row < 0) {
99 | return;
100 | }
101 | DMHYSite *currentUseSite = [[DMHYCoreDataStackManager sharedManager] currentUseSite];
102 | currentUseSite.isCurrentUse = @NO;
103 | DMHYSite *site = self.sites[row];
104 | site.isCurrentUse = @(state);
105 | [[DMHYCoreDataStackManager sharedManager] saveContext];
106 | [self.tableView reloadData];
107 | [DMHYNotification postNotificationName:DMHYSearchSiteChangedNotification object:site];
108 | }
109 |
110 | -(NSArray *)sites {
111 | if (!_sites) {
112 | _sites = [[DMHYCoreDataStackManager sharedManager] allSites];
113 | }
114 | return _sites;
115 | }
116 |
117 | - (NSString *)identifier {
118 | return @"SitesPreferences";
119 | }
120 |
121 | - (NSImage *)toolbarItemImage {
122 | return [NSImage imageNamed:NSImageNameNetwork];
123 | }
124 |
125 | - (NSString *)toolbarItemLabel {
126 | return @"站点";
127 | }
128 |
129 | @end
130 |
--------------------------------------------------------------------------------
/DMHY/SavedDataViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SavedDataViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/25/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SavedDataViewController : NSViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/SavedDataViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SavedDataViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/25/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "SavedDataViewController.h"
10 | #import "DMHYAPI.h"
11 | #import "DMHYCoreDataStackManager.h"
12 | #import "DMHYTorrent.h"
13 | #import "DMHYTool.h"
14 | #import "PreferenceController.h"
15 |
16 | @interface SavedDataViewController ()
17 |
18 | @property (weak) IBOutlet NSTableView *tableView;
19 |
20 | @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
21 | @property (nonatomic, strong) NSMutableArray *torrents;
22 |
23 | @end
24 |
25 | @implementation SavedDataViewController
26 |
27 | @synthesize managedObjectContext = _context;
28 |
29 | - (void)viewDidLoad {
30 | [super viewDidLoad];
31 | [self observeNotification];
32 | [self setupTableViewStyle];
33 | [self setupTableViewSort];
34 | [self setupMenuItems];
35 | }
36 |
37 | #pragma mark - Setup
38 |
39 | - (void)setupTableViewStyle {
40 | self.tableView.rowSizeStyle = [self preferedRowSizeStyle];
41 | self.tableView.usesAlternatingRowBackgroundColors = YES;
42 | self.tableView.columnAutoresizingStyle = NSTableViewUniformColumnAutoresizingStyle;
43 | [self.tableView sizeLastColumnToFit];
44 | }
45 |
46 | - (void)setupTableViewSort {
47 | [self.tableView setSortDescriptors:[NSArray arrayWithObjects:
48 | [NSSortDescriptor sortDescriptorWithKey:@"pubDate" ascending:YES selector:@selector(compare:)],
49 | [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES selector:@selector(compare:)],
50 | nil]];
51 | }
52 |
53 | - (void)setupMenuItems {
54 | NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
55 | NSMenuItem *editMenuItem = [mainMenu itemWithTitle:@"Edit"];
56 | NSMenu *editSubMenu = [editMenuItem submenu];
57 | unichar deleteKey = NSBackspaceCharacter;
58 | NSString *delete = [NSString stringWithCharacters:&deleteKey length:1];
59 | NSMenuItem *removeTorrentMenuItem = [[NSMenuItem alloc] initWithTitle:@"删除种子数据"
60 | action:@selector(deleteSelectTorrent)
61 | keyEquivalent:delete];
62 | removeTorrentMenuItem.keyEquivalentModifierMask = NSCommandKeyMask | NSShiftKeyMask;
63 |
64 | [editSubMenu addItem:removeTorrentMenuItem];
65 | }
66 |
67 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
68 | NSString *title = [menuItem title];
69 | if ([title isEqualToString:@"删除种子数据"]) {
70 | //什么都没选的时候
71 | if (self.tableView.selectedRow == -1) {
72 | return NO;
73 | }
74 | }
75 | return YES;
76 | }
77 |
78 | - (NSTableViewRowSizeStyle )preferedRowSizeStyle {
79 | NSInteger rowStyle = [PreferenceController viewPreferenceTableViewRowStyle];
80 | switch (rowStyle) {
81 | case 0:
82 | return NSTableViewRowSizeStyleSmall;
83 | case 1:
84 | return NSTableViewRowSizeStyleMedium;
85 | case 2:
86 | return NSTableViewRowSizeStyleLarge;
87 | default:
88 | break;
89 | }
90 | return NSTableViewRowSizeStyleSmall;
91 | }
92 |
93 | #pragma mark - NSTableViewDelegate
94 |
95 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
96 | NSString *identifier = tableColumn.identifier;
97 | DMHYTorrent *torrent = self.torrents[row];
98 | if ([identifier isEqualToString:@"pubDateCell"]) {
99 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"pubDateCell" owner:self];
100 | NSString *pubDate = [[DMHYTool tool] stringFromSavedDate:torrent.pubDate];
101 | cellView.textField.stringValue = pubDate ? pubDate : @"";
102 | return cellView;
103 | }
104 | if ([identifier isEqualToString:@"titleCell"]) {
105 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"titleCell" owner:self];
106 | cellView.textField.stringValue = torrent.title ? torrent.title : @"";
107 | return cellView;
108 | }
109 |
110 | if ([identifier isEqualToString:@"linkCell"]) {
111 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"linkCell" owner:self];
112 | cellView.textField.stringValue = torrent.link ? torrent.link : @"";
113 | return cellView;
114 | }
115 | if ([identifier isEqualToString:@"magnetCell"]) {
116 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"magnetCell" owner:self];
117 | cellView.textField.stringValue = torrent.magnet ? torrent.magnet : @"";
118 | return cellView;
119 | }
120 | return nil;
121 | }
122 |
123 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
124 | return self.torrents.count;
125 | }
126 |
127 | - (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
128 | }
129 |
130 | - (void)deleteSelectTorrent {
131 | if (self.tableView.selectedRow < 0) {
132 | return;
133 | }
134 | NSIndexSet *selectSet = [self.tableView selectedRowIndexes];
135 | [selectSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
136 | DMHYTorrent *torrent = self.torrents[idx];
137 | [self.managedObjectContext deleteObject:torrent];
138 | }];
139 | NSError *error = nil;
140 | if ([self.managedObjectContext hasChanges]) {
141 | if (![self.managedObjectContext save:&error]) {
142 | [NSApp presentError:error];
143 | };
144 | }
145 | [self.torrents removeObjectsAtIndexes:selectSet];
146 | [self.tableView reloadData];
147 | }
148 |
149 | - (void)observeNotification {
150 | [DMHYNotification addObserver:self selector:@selector(handleThemeChanged) name:DMHYThemeChangedNotification];
151 | [DMHYNotification addObserver:self selector:@selector(handleMainTableViewRowStyleChanged) name:DMHYMainTableViewRowStyleChangedNotification];
152 | [DMHYNotification addObserver:self selector:@selector(handleDatabaseChanged) name:DMHYDatabaseChangedNotification];
153 | }
154 |
155 | - (void)handleDatabaseChanged {
156 | self.torrents = nil;
157 | [self.tableView reloadData];
158 | }
159 |
160 | - (void)handleThemeChanged {
161 | [self.view setNeedsDisplay:YES];
162 | }
163 |
164 | - (void)handleMainTableViewRowStyleChanged {
165 | [self setupTableViewStyle];
166 | [self.tableView setNeedsDisplay:YES];
167 | }
168 |
169 | - (NSMutableArray *)torrents {
170 | if (!_torrents) {
171 | NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:DMHYTorrentEntityKey];
172 | request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"pubDate" ascending:NO]];
173 | _torrents = [[self.managedObjectContext executeFetchRequest:request
174 | error:NULL] mutableCopy];
175 | }
176 | return _torrents;
177 | }
178 |
179 | - (NSManagedObjectContext *)managedObjectContext {
180 | if (!_context) {
181 | _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
182 | _context.persistentStoreCoordinator = [[DMHYCoreDataStackManager sharedManager] persistentStoreCoordinator];
183 | _context.undoManager = nil;
184 | }
185 | return _context;
186 | }
187 | @end
188 |
--------------------------------------------------------------------------------
/DMHY/SideViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SideViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/19/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /**
12 | * 已保存的关键字
13 | */
14 | @interface SideViewController : NSViewController
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/DMHY/SideViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SideViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/19/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "SideViewController.h"
10 | #import "DMHYCoreDataStackManager.h"
11 |
12 | @interface SideViewController ()
13 |
14 | @property (weak) IBOutlet NSPopUpButton *sitesPopUpButton;
15 | @property (weak) IBOutlet NSOutlineView *outlineView;
16 |
17 | @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
18 | @property (nonatomic, strong ) NSMutableArray *keywords;
19 | @property (nonatomic, strong) DMHYSite *site;
20 |
21 | @end
22 |
23 | @implementation SideViewController
24 |
25 | @synthesize managedObjectContext = _context;
26 |
27 | - (void)viewDidLoad {
28 | [super viewDidLoad];
29 | [self observeNotification];
30 | [self setupPopupButtonData];
31 | [self expandOutlineViewItem];
32 | [self setupMenuItems];
33 | }
34 |
35 | - (void)expandOutlineViewItem {
36 | for (int i = 0 ; i < self.keywords.count; i++) {
37 | [self.outlineView expandItem:self.keywords[i]];
38 | }
39 | }
40 |
41 | #pragma mark - IBAction
42 |
43 |
44 | - (IBAction)siteChanged:(NSPopUpButton *)sender {
45 | NSMenuItem *item = [self.sitesPopUpButton selectedItem];
46 | NSInteger selectIndex = [self.sitesPopUpButton indexOfItem:item];
47 | DMHYSite *site = [[[DMHYCoreDataStackManager sharedManager] allSites] objectAtIndex:selectIndex];
48 | self.site = site;
49 | self.keywords = nil;
50 | [self reloadData];
51 | [DMHYNotification postNotificationName:DMHYSearchSiteChangedNotification object:site];
52 | }
53 |
54 | #pragma mark - Setup Data
55 |
56 | - (void)setupPopupButtonData {
57 | NSMutableArray *siteNames = [[NSMutableArray alloc] init];
58 | NSArray *sites = [[DMHYCoreDataStackManager sharedManager] allSites];
59 | [sites enumerateObjectsUsingBlock:^(DMHYSite * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
60 | [siteNames addObject:obj.name];
61 | }];
62 | [self.sitesPopUpButton addItemsWithTitles:siteNames];
63 | [self.sitesPopUpButton selectItemWithTitle:self.site.name];
64 | }
65 |
66 | - (void)setupMenuItems {
67 | NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
68 | NSMenuItem *editMenuItem = [mainMenu itemWithTitle:@"Edit"];
69 | NSMenu *editSubMenu = [editMenuItem submenu];
70 | unichar deleteKey = NSBackspaceCharacter;
71 | NSString *delete = [NSString stringWithCharacters:&deleteKey length:1];
72 | NSMenuItem *removeSubKeywordMenuItem = [[NSMenuItem alloc] initWithTitle:@"删除关键字"
73 | action:@selector(deleteSubKeyword)
74 | keyEquivalent:delete];
75 | [editSubMenu addItem:removeSubKeywordMenuItem];
76 | }
77 |
78 | #pragma mark - NSOutlineViewDataSource
79 |
80 | - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(DMHYKeyword *) item {
81 | return !item ? [self.keywords count] : [item.subKeywords count];
82 | }
83 |
84 | - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(DMHYKeyword *)item {
85 | return !item ? YES : [item.subKeywords count] != 0;
86 | }
87 |
88 | - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(DMHYKeyword *)item {
89 | // NSLog(@"item -> keyword %@",item.keyword);
90 | NSArray *subKeywords = [item.subKeywords allObjects];
91 | return !item ? self.keywords[index] : subKeywords[index];
92 | }
93 |
94 | - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
95 | DMHYKeyword *weekday = item;
96 | NSString *identifier = tableColumn.identifier;
97 | if ([identifier isEqualToString:@"Keyword"]) {
98 | return weekday.keyword;
99 | }
100 | return nil;
101 | }
102 |
103 | - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
104 | DMHYKeyword *keyword = item;
105 | NSString *newKeyword = object;
106 |
107 | if (!keyword.isSubKeyword.boolValue || [newKeyword isEqualToString:@""]) {
108 | return;
109 | }
110 |
111 | NSString *identifier = tableColumn.identifier;
112 | if ([identifier isEqualToString:@"Keyword"]) {
113 | keyword.keyword = newKeyword;
114 | [self saveData];
115 | }
116 | }
117 |
118 | #pragma mark - NSOutlineViewDelegate
119 |
120 | - (void)outlineViewSelectionDidChange:(NSNotification *)notification {
121 | DMHYKeyword *selectedKeyword = [self.outlineView itemAtRow:[self.outlineView selectedRow]];
122 | NSString *keyword = selectedKeyword.keyword;
123 | if (!selectedKeyword.isSubKeyword.boolValue || keyword.length == 0) {
124 | return;
125 | }
126 | NSNumber *isSubKeyword = selectedKeyword.isSubKeyword;
127 | NSDictionary *userInfo = @{kSelectKeyword : keyword,
128 | kSelectKeywordIsSubKeyword : isSubKeyword};
129 | [DMHYNotification postNotificationName:DMHYSelectKeywordChangedNotification object:selectedKeyword userInfo:userInfo];
130 | }
131 |
132 | - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item {
133 | return YES;
134 | }
135 |
136 | - (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item {
137 | return ((DMHYKeyword *)item).isSubKeyword.boolValue ? YES : NO;
138 | }
139 |
140 | #pragma mark - NSSplitViewDelegate
141 |
142 | - (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex {
143 | return NSRectFromCGRect(CGRectZero);
144 | }
145 |
146 | #pragma mark - Notification
147 |
148 | - (void)observeNotification {
149 | [DMHYNotification addObserver:self selector:@selector(handleSeedDataComplete) name:DMHYSeedDataCompletedNotification];
150 | [DMHYNotification addObserver:self selector:@selector(handleThemeChanged) name:DMHYThemeChangedNotification];
151 | [DMHYNotification addObserver:self selector:@selector(handleKeywordAdded:) name:DMHYKeywordAddedNotification];
152 | [DMHYNotification addObserver:self selector:@selector(handleSiteAdded) name:DMHYSiteAddedNotification];
153 | }
154 |
155 | - (void)handleSiteAdded {
156 | [self reset];
157 | [self setupPopupButtonData];
158 | }
159 |
160 | - (void)handleKeywordAdded:(NSNotification *)notification {
161 | DMHYSite *site = notification.object;
162 | if ([site.name isEqualToString:self.site.name]) {
163 | self.site = site;
164 | self.keywords = nil;
165 | [self reloadData];
166 | }
167 | }
168 |
169 | - (void)reset {
170 | self.site = nil;
171 | self.keywords = nil;
172 | [self reloadData];
173 | }
174 |
175 | - (void)handleSeedDataComplete {
176 | [self reset];
177 | [self setupPopupButtonData];
178 | }
179 |
180 | - (void)handleThemeChanged {
181 | [self.view setNeedsDisplay:YES];
182 | }
183 |
184 | #pragma mark - MenuItem
185 |
186 | - (void)deleteSubKeyword {
187 | NSInteger selectKeywordIndex = [self.outlineView selectedRow];
188 | DMHYKeyword *keyword = [self.outlineView itemAtRow:selectKeywordIndex];
189 | if ([keyword.isSubKeyword boolValue]) {
190 | DMHYKeyword *parentKeyword = [self.outlineView parentForItem:keyword];
191 | [parentKeyword removeSubKeywordsObject:keyword];
192 | [self.managedObjectContext deleteObject:keyword];
193 | [self saveData];
194 | [self reloadData];
195 | [DMHYNotification postNotificationName:DMHYDatabaseChangedNotification];
196 | }
197 | }
198 |
199 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
200 | NSString *title = [menuItem title];
201 | if ([title isEqualToString:@"删除关键字"]) {
202 | //什么都没选的时候
203 | if (self.outlineView.selectedRow == -1) {
204 | return NO;
205 | }
206 | DMHYKeyword *keyword = [self.outlineView itemAtRow:[self.outlineView selectedRow]];
207 | //记得使用 boolValue 获取 BOOL 值 >_<
208 | if (![keyword.isSubKeyword boolValue]) {
209 | return NO;
210 | }
211 | }
212 | return YES;
213 | }
214 |
215 | #pragma mark - Properties
216 |
217 | - (NSMutableArray *)keywords {
218 |
219 | if (!_keywords) {
220 | NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:@"createDate" ascending:YES];
221 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isSubKeyword != YES"];
222 | NSArray *weekdayKeywords = [[self.site.keywords allObjects] filteredArrayUsingPredicate:predicate];
223 | _keywords = [[weekdayKeywords sortedArrayUsingDescriptors:@[sortDesc]] mutableCopy];
224 | }
225 | return _keywords;
226 | }
227 |
228 | - (DMHYSite *)site {
229 | if (!_site) {
230 | _site = [[DMHYCoreDataStackManager sharedManager] currentUseSite];
231 | }
232 | return _site;
233 | }
234 |
235 | - (NSManagedObjectContext *)managedObjectContext {
236 | if (!_context) {
237 | _context = [[DMHYCoreDataStackManager sharedManager] managedObjectContext];
238 | }
239 | return _context;
240 | }
241 |
242 | #pragma mark - Utils
243 |
244 | - (void)saveData {
245 | NSError *error = nil;
246 | if ([self.managedObjectContext hasChanges]) {
247 | if (![self.managedObjectContext save:&error]) {
248 | NSAlert *alert = [[NSAlert alloc] init];
249 | [alert setMessageText:@"删除关键字时出现错误"];
250 | [alert setInformativeText:[NSString stringWithFormat:@"%@", [error localizedDescription]]];
251 | [alert addButtonWithTitle:@"OK"];
252 | [alert setAlertStyle:NSWarningAlertStyle];
253 |
254 | [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) {
255 | if (returnCode == NSAlertFirstButtonReturn) {
256 |
257 | }
258 | }];
259 | }
260 | }
261 | }
262 |
263 | - (void)reloadData {
264 | [self.outlineView reloadData];
265 | [self expandOutlineViewItem];
266 | }
267 |
268 | @end
269 |
--------------------------------------------------------------------------------
/DMHY/TitleTableCellView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TitleTableCellView.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/8/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TitleTableCellView : NSTableCellView
12 | @property (weak) IBOutlet NSButton *downloadButton;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/DMHY/TitleTableCellView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TitleTableCellView.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/8/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "TitleTableCellView.h"
10 |
11 | @implementation TitleTableCellView
12 |
13 | - (void)drawRect:(NSRect)dirtyRect {
14 | [super drawRect:dirtyRect];
15 |
16 | // Drawing code here.
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/DMHY/TorrentItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // TorrentItem.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 15/8/31.
6 | // Copyright © 2015年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TorrentItem : NSObject
12 |
13 | @property (nonatomic, strong) NSString *title;
14 | @property (nonatomic, strong) NSURL *link;
15 | @property (nonatomic, strong) NSURL *magnet;
16 | @property (nonatomic, strong) NSString *pubDate;
17 | @property (nonatomic, strong) NSString *author;
18 | @property (nonatomic, strong) NSString *category;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/DMHY/TorrentItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // TorrentItem.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 15/8/31.
6 | // Copyright © 2015年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "TorrentItem.h"
10 |
11 | @implementation TorrentItem
12 |
13 |
14 | - (NSString *)description {
15 | return [NSString stringWithFormat:@"PublishDate: %@ \n Title: %@ \n Author: %@ \n",self.pubDate,self.title,self.author];
16 | }
17 |
18 | //- (id)valueForUndefinedKey:(NSString *)key {
19 | //
20 | // return nil;
21 | //}
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/DMHY/View/ButtonTableCellView.h:
--------------------------------------------------------------------------------
1 | //
2 | // CheckTableCellView.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/17/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ButtonTableCellView : NSTableCellView
12 |
13 | @property (weak) IBOutlet NSButton *checkButton;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/DMHY/View/ButtonTableCellView.m:
--------------------------------------------------------------------------------
1 | //
2 | // CheckTableCellView.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 7/17/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "ButtonTableCellView.h"
10 |
11 | @implementation ButtonTableCellView
12 |
13 | - (void)drawRect:(NSRect)dirtyRect {
14 | [super drawRect:dirtyRect];
15 |
16 | // Drawing code here.
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/DMHY/View/FileTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // FileTableView.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/27/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface FileTableView : NSTableView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/View/FileTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // FileTableView.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/27/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "FileTableView.h"
10 | #import "AppDelegate.h"
11 |
12 | @implementation FileTableView
13 |
14 | - (void)keyDown:(NSEvent *)theEvent
15 | {
16 | NSString *key = [theEvent charactersIgnoringModifiers];
17 | if ([key isEqual:@" "])
18 | {
19 | AppDelegate *delegate = [[NSApplication sharedApplication] delegate];
20 | [delegate togglePreviewPanel:self];
21 | }
22 | else
23 | {
24 | [super keyDown:theEvent];
25 | }
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/DMHY/View/NSTableView+ContextMenu.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+ContextMenu.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/22/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol ContextMenuDelegate
12 | - (NSMenu*)tableView:(NSTableView*)aTableView menuForRows:(NSIndexSet*)rows;
13 | @end
14 |
15 | @interface NSTableView (ContextMenu)
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/DMHY/View/NSTableView+ContextMenu.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+ContextMenu.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 3/22/16.
6 | // Copyright © 2016 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "NSTableView+ContextMenu.h"
10 |
11 | @implementation NSTableView (ContextMenu)
12 |
13 | - (NSMenu*)menuForEvent:(NSEvent*)event
14 | {
15 | NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
16 | NSInteger row = [self rowAtPoint:location];
17 | if (!(row >= 0) || ([event type] != NSRightMouseDown)) {
18 | return [super menuForEvent:event];
19 | }
20 | NSIndexSet *selected = [self selectedRowIndexes];
21 | if (![selected containsIndex:row]) {
22 | selected = [NSIndexSet indexSetWithIndex:row];
23 | [self selectRowIndexes:selected byExtendingSelection:NO];
24 | }
25 | if ([[self delegate] respondsToSelector:@selector(tableView:menuForRows:)]) {
26 | return [(id)[self delegate] tableView:self menuForRows:selected];
27 | }
28 | return [super menuForEvent:event];
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/DMHY/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 15/8/30.
6 | // Copyright © 2015年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : NSViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/DMHY/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 15/8/30.
6 | // Copyright © 2015年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "PreferenceController.h"
11 | #import "SitePreferenceController.h"
12 | #import "DMHYCoreDataStackManager.h"
13 | #import "TorrentItem.h"
14 | #import "TitleTableCellView.h"
15 | #import "NavigationView.h"
16 | #import "DMHYXMLDataManager.h"
17 | #import "DMHYJSONDataManager.h"
18 | #import "NSTableView+ContextMenu.h"
19 | #import
20 | #import "DMHYSiteChecker.h"
21 |
22 | /**
23 | * 加载数据步骤:
24 | 1. 判断当前站点返回是 XML 还是 JSON 数据
25 | 2. 调用相应的解析方法
26 | 3. 解析完毕,重新载入 tableview
27 |
28 | 下载动漫花园种子:
29 | 1. 获取到该条目的介绍页面
30 | 2. 解析介绍页面的 HTML DOM 用 XPath 提取出种子链接
31 | 3. 调用下载种子文件的方法(DMHYDownloader downloadTorrentWithURL:)
32 |
33 | */
34 | @interface ViewController()
35 |
36 | @property (weak) IBOutlet NSTableView *tableView;
37 | @property (weak) IBOutlet NSProgressIndicator *indicator;
38 |
39 | @property (weak) IBOutlet NSTextField *searchTextField;
40 | @property (weak) IBOutlet NSTextField *info;
41 |
42 | @property (nonatomic, strong) NSMutableArray *torrents;
43 | @property (nonatomic, strong) NSString *searchURLString;
44 |
45 | @property (nonatomic, strong) DMHYSite *site;
46 |
47 | @property (nonatomic) BOOL isMagnetLink;
48 | @property (nonatomic) BOOL isSubKeyword;
49 |
50 | @property (nonatomic) NSInteger fetchInterval;
51 | @property (nonatomic) NSInteger currentRow;
52 |
53 | @property (nonatomic, strong) DMHYSiteChecker *checker;
54 |
55 | @end
56 |
57 | @implementation ViewController
58 |
59 | - (void)viewDidLoad {
60 | [super viewDidLoad];
61 | [self observeNotification];
62 | [self registeAppDefaults];
63 | [[DMHYCoreDataStackManager sharedManager] seedDataIfNeed];
64 | [self setupPreference];
65 | [self setupTableViewStyle];
66 | [self setupData:self];
67 | [self setupRepeatTask];
68 | [self setupMenuItems];
69 | [self setupTableViewDoubleAction];
70 | }
71 |
72 | - (void)setRepresentedObject:(id)representedObject {
73 | [super setRepresentedObject:representedObject];
74 | }
75 |
76 | #pragma mark - Setup
77 |
78 | - (void)registeAppDefaults {
79 | NSDictionary *appDefaults = @{ FliterKeywordKey : @"",
80 | DontDownloadCollectionKey : @YES,
81 | kFetchInterval : @(15*60),
82 | kMainTableViewRowStyle :@2,
83 | kDownloadLinkType :@0,
84 | DMHYThemeKey : @1 };
85 | [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
86 | }
87 |
88 | - (void)setupTableViewStyle {
89 | self.tableView.rowSizeStyle = [self preferedRowSizeStyle];
90 | self.tableView.usesAlternatingRowBackgroundColors = YES;
91 | self.tableView.columnAutoresizingStyle = NSTableViewUniformColumnAutoresizingStyle;
92 | [self.tableView sizeLastColumnToFit];
93 | }
94 |
95 | - (NSTableViewRowSizeStyle )preferedRowSizeStyle {
96 | NSInteger rowStyle = [PreferenceController viewPreferenceTableViewRowStyle];
97 | switch (rowStyle) {
98 | case 0:
99 | return NSTableViewRowSizeStyleSmall;
100 | case 1:
101 | return NSTableViewRowSizeStyleMedium;
102 | case 2:
103 | return NSTableViewRowSizeStyleLarge;
104 | default:
105 | break;
106 | }
107 | return NSTableViewRowSizeStyleSmall;
108 | }
109 |
110 | /**
111 | * Retrive saved preference value and set to self variable.
112 | */
113 | - (void)setupPreference {
114 | self.isMagnetLink = [PreferenceController preferenceDownloadLinkType];
115 | self.fetchInterval = [PreferenceController preferenceFetchInterval];
116 | }
117 |
118 | /**
119 | * Configure data start point.
120 | *
121 | */
122 | - (IBAction)setupData:(id)sender {
123 | //NSLog(@"sender class %@",[sender class]);
124 | if ([sender isKindOfClass:[ViewController class]]) {
125 | self.isSubKeyword = YES;
126 | }
127 | if (!self.isSubKeyword) {
128 | return;
129 | }
130 | [self resetData];
131 | [self startAnimatingProgressIndicator];
132 | [self configureSearchURLString];
133 | [self.torrents removeAllObjects];
134 | if ([self.site.responseType isEqualToString:@"json"]) {
135 | [[DMHYJSONDataManager manager] GET:self.searchURLString success:^(NSArray *objects) {
136 | self.torrents = [objects mutableCopy];
137 | [self reloadDataAndStopIndicator];
138 | } failure:^(NSError *error) {
139 | [self setErrorInfoAndStopIndicator];
140 | }];
141 | } else {
142 | [[DMHYXMLDataManager manager] GET:self.searchURLString success:^(NSArray *objects) {
143 | self.torrents = [objects mutableCopy];
144 | [self reloadDataAndStopIndicator];
145 | } failure:^(NSError *error) {
146 | [self setErrorInfoAndStopIndicator];
147 | }];
148 | }
149 | }
150 |
151 | #pragma mark - Indicator and reload table view data
152 |
153 | - (void)reloadDataAndStopIndicator {
154 | [self stopAnimatingProgressIndicator];
155 | [self.tableView reloadData];
156 | self.info.stringValue = @"加载完成 w";
157 | }
158 |
159 | - (void)setErrorInfoAndStopIndicator {
160 | self.info.stringValue = @"网络错误喵?";
161 | [self stopAnimatingProgressIndicator];
162 | }
163 |
164 | /**
165 | * Set search url string to download site.
166 | */
167 | - (void)configureSearchURLString {
168 | if ([self isLoadHomePage]) {
169 | NSString *siteMain = self.site.mainURL;
170 | self.searchURLString = siteMain;
171 | } else {
172 | NSString *siteSearch = self.site.searchURL;
173 | self.searchURLString = [[NSString stringWithFormat:siteSearch, self.searchTextField.stringValue] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
174 | }
175 | // NSLog(@"Site Info %@ %@ %@",self.site.name,self.site.responseType, self.site.isAutoDownload);
176 | }
177 |
178 | #pragma mark - Repeat Task
179 |
180 | /**
181 | * Schedule auto download new torrent task.
182 | */
183 | - (void)setupRepeatTask {
184 | self.checker = [[DMHYSiteChecker alloc] init];
185 | [self.checker startWitchCheckInterval:self.fetchInterval];
186 | }
187 |
188 | #pragma mark - NSTableViewDataSource
189 |
190 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
191 | return self.torrents.count;
192 | }
193 |
194 | #pragma mark - NSTableViewDelegate
195 |
196 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
197 | NSString *identifier = tableColumn.identifier;
198 | TorrentItem *torrent = self.torrents[row];
199 | if ([identifier isEqualToString:@"pubDateCell"]) {
200 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"pubDateCell" owner:self];
201 | cellView.textField.stringValue = torrent.pubDate;
202 | return cellView;
203 | }
204 | if ([identifier isEqualToString:@"titleCell"]) {
205 | TitleTableCellView *cellView = [tableView makeViewWithIdentifier:@"titleCell" owner:self];
206 | cellView.textField.stringValue = torrent.title;
207 | return cellView;
208 | }
209 | if ([identifier isEqualToString:@"authorCell"]) {
210 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"authorCell" owner:self];
211 | cellView.textField.stringValue = torrent.author;
212 | return cellView;
213 | }
214 | return nil;
215 | }
216 |
217 | - (void)tableViewSelectionDidChange:(NSNotification *)notification {
218 | self.currentRow = self.tableView.selectedRow;
219 | }
220 |
221 | #pragma mark - Table View Context Menu Delegate
222 |
223 | - (NSMenu *)tableView:(NSTableView *)aTableView menuForRows:(NSIndexSet *)rows {
224 | NSMenu *rightClickMenu = [[NSMenu alloc] initWithTitle:@""];
225 | NSMenuItem *openItem = [[NSMenuItem alloc] initWithTitle:@"打开介绍页面"
226 | action:@selector(openTorrentLink:)
227 | keyEquivalent:@""];
228 | NSMenuItem *downloadItem = [[NSMenuItem alloc] initWithTitle:@"下载"
229 | action:@selector(queryDownloadURL:)
230 | keyEquivalent:@""];
231 | [rightClickMenu addItem:openItem];
232 | [rightClickMenu addItem:downloadItem];
233 | return rightClickMenu;
234 | }
235 |
236 | #pragma mark - Notification
237 |
238 | - (void)observeNotification {
239 | [DMHYNotification addObserver:self selector:@selector(handleDownloadTypeChanged) name:DMHYDownloadLinkTypeNotification];
240 | [DMHYNotification addObserver:self selector:@selector(handleAutoDownloadSiteChanged) name:DMHYAutoDownloadSiteChangedNotification];
241 | [DMHYNotification addObserver:self selector:@selector(handleSelectKeywordChanged:) name:DMHYSelectKeywordChangedNotification];
242 | [DMHYNotification addObserver:self selector:@selector(handleFetchIntervalChanged) name:DMHYFetchIntervalChangedNotification];
243 | [DMHYNotification addObserver:self selector:@selector(handleThemeChanged) name:DMHYThemeChangedNotification];
244 | [DMHYNotification addObserver:self selector:@selector(handleDoubleActionChanged) name:DMHYDoubleActionChangedNotification];
245 | [DMHYNotification addObserver:self selector:@selector(handleMainTableViewRowStyleChanged) name:DMHYMainTableViewRowStyleChangedNotification];
246 | [DMHYNotification addObserver:self selector:@selector(handleDontDownloadCollectionChanged) name:DMHYDontDownloadCollectionKeyDidChangedNotification];
247 | [DMHYNotification addObserver:self selector:@selector(handleSearchSiteChanged:) name:DMHYSearchSiteChangedNotification];
248 | [DMHYNotification addObserver:self selector:@selector(handleKeywordChecked:) name:DMHYKeywordCheckedNotification];
249 | }
250 |
251 | - (void)handleKeywordChecked:(NSNotification *)notification {
252 | NSDictionary *userInfo = notification.userInfo;
253 | NSString *infoText = [NSString stringWithFormat:@"%@ %@ 检查完毕", userInfo[@"site"], userInfo[@"keyword"]];
254 | self.info.stringValue = infoText;
255 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
256 | NSDate *time = [NSDate new];
257 | self.info.stringValue = [NSString stringWithFormat:@" 上次检查时间 %@",[[DMHYTool tool] infoDateStringFromDate:time]];
258 | });
259 | }
260 |
261 | - (void)handleSearchSiteChanged:(NSNotification *)notification {
262 | DMHYSite *site = notification.object;
263 | self.site = site;
264 | [self setupData:self];
265 | }
266 |
267 | - (void)resetData {
268 | [self.torrents removeAllObjects];
269 | [self.tableView reloadData];
270 | }
271 |
272 | - (void)handleAutoDownloadSiteChanged {
273 | [self.checker invalidateTimer];
274 | self.fetchInterval = [PreferenceController preferenceFetchInterval];
275 | [self setupRepeatTask];
276 | }
277 |
278 | - (void)handleDownloadTypeChanged {
279 | [self setupPreference];
280 | }
281 |
282 | - (void)handleDontDownloadCollectionChanged {
283 | [self setupPreference];
284 | }
285 |
286 | - (void)handleSelectKeywordChanged:(NSNotification *)noti {
287 | NSDictionary *userInfo = noti.userInfo;
288 |
289 | NSString *keywordStr = userInfo[kSelectKeyword];
290 | BOOL isSubKeywordBOOL = [userInfo[kSelectKeywordIsSubKeyword] boolValue];
291 |
292 | self.isSubKeyword = isSubKeywordBOOL;
293 | if (!isSubKeywordBOOL) {
294 | self.searchTextField.stringValue = @"";
295 | } else {
296 | self.searchTextField.stringValue = keywordStr;
297 | }
298 |
299 | [self setupData:userInfo];
300 | }
301 |
302 | - (void)handleDoubleActionChanged {
303 | [self setupTableViewDoubleAction];
304 | }
305 |
306 | - (void)setupTableViewDoubleAction {
307 | NSInteger action = [PreferenceController preferenceDoubleAction];
308 | switch (action) {
309 | case 0:
310 | self.tableView.doubleAction = @selector(openTorrentLink:);
311 | break;
312 | case 1:
313 | self.tableView.doubleAction = @selector(queryDownloadURL:);
314 | break;
315 | default:
316 | break;
317 | }
318 |
319 | }
320 |
321 | - (void)handleFetchIntervalChanged {
322 | [self.checker invalidateTimer];
323 | self.fetchInterval = [PreferenceController preferenceFetchInterval];
324 | [self setupRepeatTask];
325 | }
326 |
327 | - (void)handleThemeChanged {
328 | [self.view setNeedsDisplay:YES];
329 | }
330 |
331 | - (void)handleMainTableViewRowStyleChanged {
332 | [self setupTableViewStyle];
333 | [self.tableView setNeedsDisplay:YES];
334 | }
335 |
336 | #pragma mark - Property Initialization
337 |
338 | - (NSMutableArray *)torrents {
339 | if (!_torrents) {
340 | _torrents = [NSMutableArray array];
341 | }
342 | return _torrents;
343 | }
344 |
345 | - (DMHYSite *)site {
346 | if (!_site) {
347 | _site = [[DMHYCoreDataStackManager sharedManager] currentUseSite];
348 | }
349 | return _site;
350 | }
351 |
352 | - (NSString *)searchURLString {
353 | if (!_searchURLString) {
354 | _searchURLString = [NSString new];
355 | }
356 | return _searchURLString;
357 | }
358 |
359 | /**
360 | * 判断搜索关键字是否为空,如果为空的话加载主页,否则加载搜索页
361 | *
362 | * @return YES 加载主页数据
363 | */
364 | - (BOOL)isLoadHomePage {
365 | return [self.searchTextField.stringValue isEqualToString:@""] ? YES : NO;
366 | }
367 |
368 | #pragma mark - Download
369 |
370 | /**
371 | * Query clicked row torrent download url then download torrent.
372 | *
373 | * @param sender
374 | */
375 | - (IBAction)queryDownloadURL:(id)sender {
376 | NSInteger row = -1;
377 | if ([sender isKindOfClass:[NSButton class]]) {
378 | row = [self.tableView rowForView:sender];
379 | } else {
380 | row = self.currentRow;
381 | }
382 | [self startAnimatingProgressIndicator];
383 | TorrentItem *item = (TorrentItem *)[self.torrents objectAtIndex:row];
384 | if ([self.site.name isEqualToString:@"dmhy"]) {
385 | if (!self.isMagnetLink) {
386 | [[DMHYDownloader downloader] downloadTorrentFromPageURLString:item.link.absoluteString willStartBlock:^{
387 | [self startAnimatingProgressIndicator];
388 | } success:^{
389 | dispatch_async(dispatch_get_main_queue(), ^{
390 | [self stopAnimatingProgressIndicator];
391 | });
392 | } failure:^(NSError *error) {
393 | NSLog(@"Error %@",[error localizedDescription]);
394 | [self setErrorInfoAndStopIndicator];
395 | }];
396 | return;
397 | }
398 | }
399 | if ([self.site.name containsString:@"nyaa.se"]) {
400 | [[DMHYDownloader downloader] downloadTorrentWithURL:item.link];
401 | [self stopAnimatingProgressIndicator];
402 | return;
403 | }
404 | // acg.rip contains .torrent bt.acg.gg contains down.php
405 | if ([item.magnet.absoluteString containsString:@".torrent"] ||
406 | [item.magnet.absoluteString containsString:@"down.php"]) {
407 | [[DMHYDownloader downloader] downloadTorrentWithURL:item.magnet];
408 | [self stopAnimatingProgressIndicator];
409 | return;
410 | }
411 | if ([item.magnet.absoluteString containsString:@"magnet:?xt=urn:btih:"]) {
412 | [self stopAnimatingProgressIndicator];
413 | [self openMagnetWith:item.magnet];
414 | return;
415 | }
416 | }
417 |
418 | - (void)openMagnetWith:(NSURL *)magnet {
419 | [[NSWorkspace sharedWorkspace] openURL:magnet];
420 | }
421 |
422 |
423 | #pragma mark - NSTextFieldDelegate
424 |
425 | - (void)controlTextDidEndEditing:(NSNotification *)notification {
426 | if ([notification.userInfo[@"NSTextMovement"] intValue] == NSReturnTextMovement) {
427 | [self setupData:self];
428 | }
429 | }
430 |
431 |
432 | #pragma mark - ProgressIndicator
433 |
434 | - (void)startAnimatingProgressIndicator {
435 | [self.indicator startAnimation:self];
436 | self.info.stringValue = @"";
437 | }
438 |
439 | - (void)stopAnimatingProgressIndicator {
440 | [self.indicator stopAnimation:self];
441 | }
442 |
443 | #pragma mark - Utils
444 |
445 | /**
446 | * Open Torrent Description Page in default browser.
447 | *
448 | * @param sender
449 | */
450 | - (void)openTorrentLink:(id) sender {
451 | NSInteger rowIndex = self.currentRow;
452 | if (rowIndex < 0) {
453 | //Click column header do nothing.
454 | return;
455 | }
456 | TorrentItem *item = self.torrents[rowIndex];
457 | [[NSWorkspace sharedWorkspace] openURL:item.link];
458 | }
459 |
460 | - (void)setupAutomaticDownloadNewTorrent {
461 | [self.checker automaticDownloadTorrent];
462 |
463 | }
464 |
465 | - (void)setupMenuItems {
466 | NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
467 | NSMenuItem *fileMenuItem = [mainMenu itemWithTitle:@"File"];
468 | NSMenu *fileSubMenu = [fileMenuItem submenu];
469 | // 设置快捷键时默认 mask 是 command,然后加单键使用小写字母(如:@“r” == cmd+r),想要多加个 shift mask,用大写字母。(如:@“R” == cmd+shift+r)
470 | NSMenuItem *menuRefreshSubKeywordMenuItem = [[NSMenuItem alloc] initWithTitle:@"手动检查订阅更新"
471 | action:@selector(setupAutomaticDownloadNewTorrent)
472 | keyEquivalent:@"r"];
473 | [fileSubMenu addItem:menuRefreshSubKeywordMenuItem];
474 |
475 | }
476 |
477 | @end
478 |
--------------------------------------------------------------------------------
/DMHY/ViewerPreferences.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/ViewerPreferences.tiff
--------------------------------------------------------------------------------
/DMHY/WindowController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WindowController.h
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/3/7.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WindowController : NSWindowController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/DMHY/WindowController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WindowController.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 16/3/7.
6 | // Copyright © 2016年 yaqinking. All rights reserved.
7 | //
8 |
9 | #import "WindowController.h"
10 |
11 | @interface WindowController ()
12 |
13 | @end
14 |
15 | @implementation WindowController
16 |
17 | - (void)windowDidLoad {
18 | [super windowDidLoad];
19 |
20 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
21 | // Set to 10.10 Yosemite style window.
22 | self.window.titleVisibility = NSWindowTitleHidden;
23 | self.window.styleMask |= NSFullSizeContentViewWindowMask;
24 | self.window.titlebarAppearsTransparent = YES;
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/DMHY/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaqinking/DMHY/82e3ef3d3f2b7a48a56ae8554053e65331cf3268/DMHY/download.png
--------------------------------------------------------------------------------
/DMHY/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // DMHY
4 | //
5 | // Created by 小笠原やきん on 9/9/15.
6 | // Copyright © 2015 yaqinking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[]) {
12 | return NSApplicationMain(argc, argv);
13 | }
14 |
--------------------------------------------------------------------------------
/DMHY/support-site.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "yaqinking",
3 | "create_at": "2016-07-16",
4 | "sites": [
5 | {
6 | "site_name": "dmhy",
7 | "main_url": "https://share.dmhy.org/topics/rss/rss.xml",
8 | "search_url": "https://share.dmhy.org/topics/rss/rss.xml?keyword=%@",
9 | "fliter_site": false,
10 | "auto_download": true,
11 | "download_type": "torrent",
12 | "download_fin": false,
13 | "response_type": "xml",
14 | "current_use": true,
15 | "keywords": {}
16 | },
17 | {
18 | "site_name": "acg.rip",
19 | "main_url": "https://acg.rip/.xml",
20 | "search_url": "https://acg.rip/.xml?term=%@",
21 | "fliter_site": false,
22 | "auto_download": false,
23 | "download_type": "torrent",
24 | "download_fin": false,
25 | "response_type": "xml",
26 | "current_use": false,
27 | "keywords": {}
28 | },
29 | {
30 | "site_name": "bangumi.moe",
31 | "main_url": "https://bangumi.moe/api/v2/torrent/page/1?limit=50",
32 | "search_url": "https://bangumi.moe/api/v2/torrent/search?limit=50&p=1&query=%@",
33 | "fliter_site": false,
34 | "auto_download": false,
35 | "download_type": "magnet",
36 | "download_fin": false,
37 | "response_type": "json",
38 | "current_use": false,
39 | "keywords": {}
40 | },
41 | {
42 | "site_name": "acg.gg",
43 | "main_url": "http://bt.acg.gg/rss.xml",
44 | "search_url": "http://bt.acg.gg/rss.xml?keyword=%@",
45 | "fliter_site": false,
46 | "auto_download": false,
47 | "download_type": "torrent",
48 | "download_fin": false,
49 | "response_type": "xml",
50 | "current_use": false,
51 | "keywords": {}
52 | },
53 | {
54 | "site_name": "kisssub.org",
55 | "main_url": "http://www.kisssub.org/rss.xml",
56 | "search_url": "http://www.kisssub.org/rss-%@.xml",
57 | "fliter_site": false,
58 | "auto_download": false,
59 | "download_type": "torrent",
60 | "download_fin": false,
61 | "response_type": "xml",
62 | "current_use": false,
63 | "keywords": {}
64 | },
65 | {
66 | "site_name": "mikanani.me",
67 | "main_url": "http://mikanani.me/RSS/Classic",
68 | "search_url": "http://mikanani.me/RSS/Search?searchstr=%@",
69 | "fliter_site": false,
70 | "auto_download": false,
71 | "download_type": "magnet",
72 | "download_fin": false,
73 | "response_type": "xml",
74 | "current_use": false,
75 | "keywords": {}
76 | },
77 | {
78 | "site_name": "nyaa.si",
79 | "main_url": "http://www.nyaa.si/?page=rss",
80 | "search_url": "http://www.nyaa.si/?page=rss&term=%@",
81 | "fliter_site": false,
82 | "auto_download": false,
83 | "download_type": "torrent",
84 | "download_fin": false,
85 | "response_type": "xml",
86 | "current_use": false,
87 | "keywords": {}
88 | }
89 | ]
90 | }
91 |
--------------------------------------------------------------------------------
/DMHY/zh-Hans.lproj/Main.strings:
--------------------------------------------------------------------------------
1 |
2 | /* Class = "NSMenuItem"; title = "DMHY"; ObjectID = "1Xt-HY-uBw"; */
3 | "1Xt-HY-uBw.title" = "DMHY";
4 |
5 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "3Zv-Lh-hMH"; */
6 | "3Zv-Lh-hMH.title" = "Table View Cell";
7 |
8 | /* Class = "NSMenuItem"; title = "Quit DMHY"; ObjectID = "4sb-4s-VLi"; */
9 | "4sb-4s-VLi.title" = "Quit DMHY";
10 |
11 | /* Class = "NSMenuItem"; title = "About DMHY"; ObjectID = "5kV-Vb-QxS"; */
12 | "5kV-Vb-QxS.title" = "About DMHY";
13 |
14 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "6DS-Xm-Q9b"; */
15 | "6DS-Xm-Q9b.title" = "Table View Cell";
16 |
17 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "6jV-1H-fgd"; */
18 | "6jV-1H-fgd.title" = "Table View Cell";
19 |
20 | /* Class = "NSTextFieldCell"; placeholderString = "输入要搜索的关键字 >_<"; ObjectID = "76V-ZO-pEU"; */
21 | "76V-ZO-pEU.placeholderString" = "输入要搜索的关键字 >_<";
22 |
23 | /* Class = "NSMenuItem"; title = "Fetch All Torrents Info"; ObjectID = "8pT-dL-euw"; */
24 | "8pT-dL-euw.title" = "Fetch All Torrents Info";
25 |
26 | /* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "9jA-f7-reS"; */
27 | "9jA-f7-reS.title" = "Enter Full Screen";
28 |
29 | /* Class = "NSMenuItem"; title = "打开下载到的目录"; ObjectID = "9vj-Xu-N63"; */
30 | "9vj-Xu-N63.title" = "打开下载到的目录";
31 |
32 | /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */
33 | "AYu-sK-qS6.title" = "Main Menu";
34 |
35 | /* Class = "NSTableColumn"; headerCell.title = "文件名"; ObjectID = "BGh-WO-WlU"; */
36 | "BGh-WO-WlU.headerCell.title" = "文件名";
37 |
38 | /* Class = "NSMenuItem"; title = "Preferences…"; ObjectID = "BOF-NM-1cW"; */
39 | "BOF-NM-1cW.title" = "Preferences…";
40 |
41 | /* Class = "NSButtonCell"; title = "添加所有"; ObjectID = "BfY-Rp-ZIt"; */
42 | "BfY-Rp-ZIt.title" = "添加所有";
43 |
44 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "CLH-bM-NEE"; */
45 | "CLH-bM-NEE.title" = "Table View Cell";
46 |
47 | /* Class = "NSMenuItem"; title = "Paste and Match Style"; ObjectID = "Cdz-Vl-Cgb"; */
48 | "Cdz-Vl-Cgb.title" = "Paste and Match Style";
49 |
50 | /* Class = "NSViewController"; title = "添加关键字"; ObjectID = "DTa-6Z-o28"; */
51 | "DTa-6Z-o28.title" = "添加关键字";
52 |
53 | /* Class = "NSMenuItem"; title = "关闭"; ObjectID = "DVo-aG-piG"; */
54 | "DVo-aG-piG.title" = "关闭";
55 |
56 | /* Class = "NSMenu"; title = "Help"; ObjectID = "F2S-fz-NVQ"; */
57 | "F2S-fz-NVQ.title" = "Help";
58 |
59 | /* Class = "NSMenuItem"; title = "DMHY Help"; ObjectID = "FKE-Sm-Kum"; */
60 | "FKE-Sm-Kum.title" = "DMHY Help";
61 |
62 | /* Class = "NSButtonCell"; title = "添加"; ObjectID = "GhW-r2-L8I"; */
63 | "GhW-r2-L8I.title" = "添加";
64 |
65 | /* Class = "NSMenuItem"; title = "View"; ObjectID = "H8h-7b-M4v"; */
66 | "H8h-7b-M4v.title" = "View";
67 |
68 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "HE2-g0-k6g"; */
69 | "HE2-g0-k6g.title" = "Table View Cell";
70 |
71 | /* Class = "NSTableColumn"; headerCell.title = "已保存的关键字"; ObjectID = "HdH-We-VzC"; */
72 | "HdH-We-VzC.headerCell.title" = "已保存的关键字";
73 |
74 | /* Class = "NSTableColumn"; headerCell.title = "番组名(中文)"; ObjectID = "HeL-Kb-HRu"; */
75 | "HeL-Kb-HRu.headerCell.title" = "番组名(中文)";
76 |
77 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Heg-jy-XeK"; */
78 | "Heg-jy-XeK.title" = "Text Cell";
79 |
80 | /* Class = "NSMenu"; title = "View"; ObjectID = "HyV-fh-RgO"; */
81 | "HyV-fh-RgO.title" = "View";
82 |
83 | /* Class = "NSWindow"; title = "动漫花园"; ObjectID = "IQv-IB-iLA"; */
84 | "IQv-IB-iLA.title" = "动漫花园";
85 |
86 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "Ihb-Ab-kA6"; */
87 | "Ihb-Ab-kA6.title" = "Table View Cell";
88 |
89 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "Jai-d0-HJP"; */
90 | "Jai-d0-HJP.title" = "Table View Cell";
91 |
92 | /* Class = "NSTableColumn"; headerCell.title = "字幕组"; ObjectID = "KFF-NT-9Uh"; */
93 | "KFF-NT-9Uh.headerCell.title" = "字幕组";
94 |
95 | /* Class = "NSMenuItem"; title = "Capitalize"; ObjectID = "KVi-bH-A71"; */
96 | "KVi-bH-A71.title" = "Capitalize";
97 |
98 | /* Class = "NSMenuItem"; title = "Speech"; ObjectID = "Kbv-ou-V1b"; */
99 | "Kbv-ou-V1b.title" = "Speech";
100 |
101 | /* Class = "NSMenuItem"; title = "Show All"; ObjectID = "Kd2-mp-pUS"; */
102 | "Kd2-mp-pUS.title" = "Show All";
103 |
104 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "LBn-16-CNw"; */
105 | "LBn-16-CNw.title" = "Text Cell";
106 |
107 | /* Class = "NSMenuItem"; title = "Bring All to Front"; ObjectID = "LE2-aR-0XJ"; */
108 | "LE2-aR-0XJ.title" = "Bring All to Front";
109 |
110 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Ldy-r6-eog"; */
111 | "Ldy-r6-eog.title" = "Text Cell";
112 |
113 | /* Class = "NSMenuItem"; title = "Services"; ObjectID = "NMo-om-nkz"; */
114 | "NMo-om-nkz.title" = "Services";
115 |
116 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "O8D-nH-MrJ"; */
117 | "O8D-nH-MrJ.title" = "Text Cell";
118 |
119 | /* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "OY7-WF-poV"; */
120 | "OY7-WF-poV.title" = "Minimize";
121 |
122 | /* Class = "NSButtonCell"; title = "搜索"; ObjectID = "Oa0-ov-WIx"; */
123 | "Oa0-ov-WIx.title" = "搜索";
124 |
125 | /* Class = "NSMenuItem"; title = "Hide DMHY"; ObjectID = "Olw-nP-bQN"; */
126 | "Olw-nP-bQN.title" = "Hide DMHY";
127 |
128 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "Q1g-L3-sgY"; */
129 | "Q1g-L3-sgY.title" = "Table View Cell";
130 |
131 | /* Class = "NSMenu"; title = "Edit"; ObjectID = "Qb6-ez-idR"; */
132 | "Qb6-ez-idR.title" = "Edit";
133 |
134 | /* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "R4o-n2-Eq4"; */
135 | "R4o-n2-Eq4.title" = "Zoom";
136 |
137 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RgY-0l-DSE"; */
138 | "RgY-0l-DSE.title" = "Text Cell";
139 |
140 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "RsV-x3-fOH"; */
141 | "RsV-x3-fOH.title" = "Table View Cell";
142 |
143 | /* Class = "NSMenu"; title = "Window"; ObjectID = "Td7-aD-5lo"; */
144 | "Td7-aD-5lo.title" = "Window";
145 |
146 | /* Class = "NSTableColumn"; headerCell.title = "上传者"; ObjectID = "Uhs-2l-q8U"; */
147 | "Uhs-2l-q8U.headerCell.title" = "上传者";
148 |
149 | /* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "Vdr-fp-XzO"; */
150 | "Vdr-fp-XzO.title" = "Hide Others";
151 |
152 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Wp0-Bo-uvV"; */
153 | "Wp0-Bo-uvV.title" = "Text Cell";
154 |
155 | /* Class = "NSMenuItem"; title = "Transformations"; ObjectID = "X3Q-on-iSx"; */
156 | "X3Q-on-iSx.title" = "Transformations";
157 |
158 | /* Class = "NSMenuItem"; title = "Make Lower Case"; ObjectID = "XAT-Mt-tUi"; */
159 | "XAT-Mt-tUi.title" = "Make Lower Case";
160 |
161 | /* Class = "NSTextFieldCell"; title = "Label"; ObjectID = "XgS-oG-Dbw"; */
162 | "XgS-oG-Dbw.title" = "Label";
163 |
164 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "YGo-B1-dFu"; */
165 | "YGo-B1-dFu.title" = "Text Cell";
166 |
167 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Yfq-gQ-Oxs"; */
168 | "Yfq-gQ-Oxs.title" = "Text Cell";
169 |
170 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "ZAl-C1-BWZ"; */
171 | "ZAl-C1-BWZ.title" = "Text Cell";
172 |
173 | /* Class = "NSMenuItem"; title = "Start Speaking"; ObjectID = "ZaZ-s6-4Ix"; */
174 | "ZaZ-s6-4Ix.title" = "Start Speaking";
175 |
176 | /* Class = "NSMenuItem"; title = "Window"; ObjectID = "aUF-d1-5bR"; */
177 | "aUF-d1-5bR.title" = "Window";
178 |
179 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "as8-GI-Rmc"; */
180 | "as8-GI-Rmc.title" = "Table View Cell";
181 |
182 | /* Class = "NSTableColumn"; headerCell.title = "搜索关键字"; ObjectID = "baj-mN-cht"; */
183 | "baj-mN-cht.headerCell.title" = "搜索关键字";
184 |
185 | /* Class = "NSMenu"; title = "File"; ObjectID = "bib-Uj-vzu"; */
186 | "bib-Uj-vzu.title" = "File";
187 |
188 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "bti-xX-z1h"; */
189 | "bti-xX-z1h.title" = "Table View Cell";
190 |
191 | /* Class = "NSTableColumn"; headerCell.title = "标题"; ObjectID = "buQ-50-SAf"; */
192 | "buQ-50-SAf.headerCell.title" = "标题";
193 |
194 | /* Class = "NSMenuItem"; title = "File"; ObjectID = "dMs-cI-mzQ"; */
195 | "dMs-cI-mzQ.title" = "File";
196 |
197 | /* Class = "NSMenu"; title = "Speech"; ObjectID = "dRL-wg-o1f"; */
198 | "dRL-wg-o1f.title" = "Speech";
199 |
200 | /* Class = "NSButtonCell"; title = "获取当前季度关键字"; ObjectID = "dln-kk-c6S"; */
201 | "dln-kk-c6S.title" = "获取当前季度关键字";
202 |
203 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "fmX-ho-FMH"; */
204 | "fmX-ho-FMH.title" = "Text Cell";
205 |
206 | /* Class = "NSMenuItem"; title = "Select All"; ObjectID = "frq-UF-PSA"; */
207 | "frq-UF-PSA.title" = "Select All";
208 |
209 | /* Class = "NSMenuItem"; title = "Copy"; ObjectID = "g6i-Wg-9XK"; */
210 | "g6i-Wg-9XK.title" = "Copy";
211 |
212 | /* Class = "NSTableColumn"; headerCell.title = "上传日期"; ObjectID = "ghi-PI-mzL"; */
213 | "ghi-PI-mzL.headerCell.title" = "上传日期";
214 |
215 | /* Class = "NSButtonCell"; title = "添加选中"; ObjectID = "gui-5b-wR1"; */
216 | "gui-5b-wR1.title" = "添加选中";
217 |
218 | /* Class = "NSTextFieldCell"; placeholderString = "输入要保存的关键字"; ObjectID = "hLY-pA-HcS"; */
219 | "hLY-pA-HcS.placeholderString" = "输入要保存的关键字";
220 |
221 | /* Class = "NSMenuItem"; title = "Cut"; ObjectID = "hVE-L4-lmD"; */
222 | "hVE-L4-lmD.title" = "Cut";
223 |
224 | /* Class = "NSMenu"; title = "Services"; ObjectID = "hz9-B4-Xy5"; */
225 | "hz9-B4-Xy5.title" = "Services";
226 |
227 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "ig0-IQ-x2A"; */
228 | "ig0-IQ-x2A.title" = "Table View Cell";
229 |
230 | /* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "j0c-XP-MwU"; */
231 | "j0c-XP-MwU.title" = "Table View Cell";
232 |
233 | /* Class = "NSMenuItem"; title = "Paste"; ObjectID = "jxD-yS-vdc"; */
234 | "jxD-yS-vdc.title" = "Paste";
235 |
236 | /* Class = "NSTableColumn"; headerCell.title = "修改日期"; ObjectID = "lUj-K9-oGh"; */
237 | "lUj-K9-oGh.headerCell.title" = "修改日期";
238 |
239 | /* Class = "NSTableColumn"; headerCell.title = "是否为新种子"; ObjectID = "mf7-I2-ees"; */
240 | "mf7-I2-ees.headerCell.title" = "是否为新种子";
241 |
242 | /* Class = "NSTableColumn"; headerCell.title = "日期"; ObjectID = "n4V-ZQ-v2F"; */
243 | "n4V-ZQ-v2F.headerCell.title" = "日期";
244 |
245 | /* Class = "NSTableColumn"; headerCell.title = "标题"; ObjectID = "nCr-UM-8pI"; */
246 | "nCr-UM-8pI.headerCell.title" = "标题";
247 |
248 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "p5N-JK-gJp"; */
249 | "p5N-JK-gJp.title" = "Text Cell";
250 |
251 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "qQb-1K-C0s"; */
252 | "qQb-1K-C0s.title" = "Text Cell";
253 |
254 | /* Class = "NSTableColumn"; headerCell.title = "周几"; ObjectID = "qle-b3-qU6"; */
255 | "qle-b3-qU6.headerCell.title" = "周几";
256 |
257 | /* Class = "NSMenuItem"; title = "Delete"; ObjectID = "sRO-AN-1gR"; */
258 | "sRO-AN-1gR.title" = "Delete";
259 |
260 | /* Class = "NSMenuItem"; title = "Edit"; ObjectID = "sUe-Si-5e1"; */
261 | "sUe-Si-5e1.title" = "Edit";
262 |
263 | /* Class = "NSTableColumn"; headerCell.title = "番组名(日文)"; ObjectID = "tAH-lS-QUg"; */
264 | "tAH-lS-QUg.headerCell.title" = "番组名(日文)";
265 |
266 | /* Class = "NSMenuItem"; title = "初始化自动下载种子数据库"; ObjectID = "twB-iS-wTW"; */
267 | "twB-iS-wTW.title" = "初始化自动下载种子数据库";
268 |
269 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "tzK-dj-QHS"; */
270 | "tzK-dj-QHS.title" = "Text Cell";
271 |
272 | /* Class = "NSMenu"; title = "DMHY"; ObjectID = "uQy-DD-JDr"; */
273 | "uQy-DD-JDr.title" = "DMHY";
274 |
275 | /* Class = "NSMenuItem"; title = "添加季度关键字"; ObjectID = "wJR-J2-GEX"; */
276 | "wJR-J2-GEX.title" = "添加季度关键字";
277 |
278 | /* Class = "NSMenuItem"; title = "Switch Theme"; ObjectID = "wM1-cj-jaY"; */
279 | "wM1-cj-jaY.title" = "Switch Theme";
280 |
281 | /* Class = "NSMenuItem"; title = "Help"; ObjectID = "wpr-3q-Mcd"; */
282 | "wpr-3q-Mcd.title" = "Help";
283 |
284 | /* Class = "NSMenuItem"; title = "Make Upper Case"; ObjectID = "x3i-38-mq2"; */
285 | "x3i-38-mq2.title" = "Make Upper Case";
286 |
287 | /* Class = "NSMenu"; title = "Transformations"; ObjectID = "xpR-Wl-Fcg"; */
288 | "xpR-Wl-Fcg.title" = "Transformations";
289 |
290 | /* Class = "NSMenuItem"; title = "Stop Speaking"; ObjectID = "xqg-b4-4uR"; */
291 | "xqg-b4-4uR.title" = "Stop Speaking";
292 |
293 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "ycD-0e-CBH"; */
294 | "ycD-0e-CBH.title" = "Text Cell";
295 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 小笠原 やきん(yaqinking)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :osx, '10.10'
3 |
4 | target 'DMHY' do
5 |
6 | pod 'AFNetworking', '~> 3.0'
7 | pod 'Ono'
8 | pod 'CDEvents'
9 | pod 'DateTools'
10 |
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFNetworking (3.1.0):
3 | - AFNetworking/NSURLSession (= 3.1.0)
4 | - AFNetworking/Reachability (= 3.1.0)
5 | - AFNetworking/Security (= 3.1.0)
6 | - AFNetworking/Serialization (= 3.1.0)
7 | - AFNetworking/UIKit (= 3.1.0)
8 | - AFNetworking/NSURLSession (3.1.0):
9 | - AFNetworking/Reachability
10 | - AFNetworking/Security
11 | - AFNetworking/Serialization
12 | - AFNetworking/Reachability (3.1.0)
13 | - AFNetworking/Security (3.1.0)
14 | - AFNetworking/Serialization (3.1.0)
15 | - CDEvents (1.2.2)
16 | - DateTools (1.7.0)
17 | - Ono (1.2.2)
18 |
19 | DEPENDENCIES:
20 | - AFNetworking (~> 3.0)
21 | - CDEvents
22 | - DateTools
23 | - Ono
24 |
25 | SPEC REPOS:
26 | trunk:
27 | - AFNetworking
28 | - CDEvents
29 | - DateTools
30 | - Ono
31 |
32 | SPEC CHECKSUMS:
33 | AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
34 | CDEvents: f311f23de13414f4b3625f1a663b2e1d9a85d982
35 | DateTools: 53288ee8b905fdc75897a1e6b5cc0144b14cba60
36 | Ono: 80b1c686777d3f6d5d5ce6fcac8a74afef4ca197
37 |
38 | PODFILE CHECKSUM: cdeda80e5c313d6f68f1c2a7f581fe7906f52591
39 |
40 | COCOAPODS: 1.10.1
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DMHY
2 | 一个订阅关键字后自动从相应站点下载新番的工具。
3 |
4 | 使用手册见 [Wiki](https://github.com/yaqinking/DMHY/wiki)
5 |
6 | 个人使用方法:把每天要看动画的关键字+字幕组+字幕类型设定好之后,每隔 60 分钟让她自动检测是否有新 ep。
7 |
8 | 软件运行系统需求:
9 | - 系统最低支持 OS X 10.10
10 | - 支持 Intel 和 Apple Silicon 处理器的 Mac
11 |
12 | 目前
13 |
14 | 支持以下站点搜索和自动下载种子或者使用 bt 客户端打开 magnet 链接
15 |
16 | - https://share.dmhy.org
17 | - https://acg.rip
18 | - http://bt.acg.gg
19 | - http://www.kisssub.org
20 | - http://mikanani.me
21 | - http://www.nyaa.si (提示:由于 nyaa 域名进行过更改,如果不是第一次使用本 app 需要重置数据库后才会更改为新域名。)
22 |
23 | 支持以下站点搜索
24 |
25 | - https://bangumi.moe
26 |
27 | 其它 RSS 站点如果支持类似 `https://share.dmhy.org/topics/rss/rss.xml?keyword=%@` 这种格式的可以尝试一下添加自定义站点看是否能够正常使用。(`%@` 代表搜索关键字)
28 |
29 | 功能:
30 |
31 | - 自动下载当天订阅的关键字更新
32 | - 导入关键字(方便批量添加,导入时如果站点不存在会添加为自定义站点)
33 | - 导出关键字(方便备份,分享给别人)
34 | - 文件管理(方便双击直接打开观看)
35 |
36 | 附加说明:
37 |
38 | - 使用 Sandbox
39 | - 使用 Apple Notarization
40 |
41 | ## Screenshot
42 | 暗色主题
43 |
44 | 
45 |
46 | 多站点自动下载开关设置
47 |
48 | 
49 |
50 | 文件管理
51 |
52 | 
53 |
54 | 导出关键字
55 |
56 | 
57 |
58 | 自动下载新种子(后台观测时,实际使用时只会在下载完成时通知。)
59 |
60 | 
61 |
62 | ## 开发环境
63 |
64 | - macOS 11.1
65 | - Xcode 12.3
66 |
67 | ## Thanks
68 |
69 | - [AFNetworking](https://github.com/AFNetworking/AFNetworking)
70 | - [Ono](https://github.com/mattt/Ono)
71 | - [AFOnoResponseSerializer](https://github.com/AFNetworking/AFOnoResponseSerializer)
72 | - [DateTools](https://github.com/MatthewYork/DateTools)
73 | - [CocoaPods](https://cocoapods.org/)
74 | - [stackoverflow](http://stackoverflow.com/)
75 | - [raywenderlich](https://www.raywenderlich.com/)
76 | - [Google search](https://www.google.com/)
77 | - [NSHipster](http://nshipster.com/)
78 | - [objc](https://www.objc.io/)
79 |
80 | and others.
81 |
82 | ## License
83 | Under MIT license, see LICENSE file.
84 |
85 |
86 |
--------------------------------------------------------------------------------