-
Notifications
You must be signed in to change notification settings - Fork 3
/
MainActivity.cs
164 lines (142 loc) · 5.39 KB
/
MainActivity.cs
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Util;
using YoutubeExplode;
using YoutubeExplode.Videos;
using YoutubeExplode.Videos.Streams;
namespace YoutubeDL {
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar")]
[IntentFilter(
new[] { Intent.ActionSend },
Categories = new[] { Intent.CategoryDefault },
DataHosts = new[] { "youtube.com", "youtu.be" },
DataMimeType = "text/plain")]
public class MainActivity : Activity {
private const string LogTag = "FOXITE_YOUTUBE_DL";
private static int s_NextNotificationID;
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
string[] requiredPermissions = new string[] { Manifest.Permission.Internet, Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage };
if (requiredPermissions.Any(perm => ContextCompat.CheckSelfPermission(this, perm) != Permission.Granted)) {
ActivityCompat.RequestPermissions(this, requiredPermissions, 0);
} else {
DoDownload();
}
}
private void DoDownload() {
Finish();
// Major threaded spaghetti below.
if (Intent?.Extras != null) {
string youtubeUrl = Intent.GetStringExtra(Intent.ExtraText);
Task.Run(async () => {
int notificationID = s_NextNotificationID++;
var manager = (NotificationManager) GetSystemService(Java.Lang.Class.FromType(typeof(NotificationManager)));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O) {
manager.CreateNotificationChannel(new NotificationChannel("youtubedl", "YoutubeDL", NotificationImportance.Low));
}
var videoId = VideoId.Parse(youtubeUrl);
NotificationCompat.Builder notif = new NotificationCompat.Builder(ApplicationContext, "youtubedl")
.SetProgress(0, 100, true)
.SetOngoing(true)
.SetSound(null)
.SetSmallIcon(Resource.Mipmap.ic_launcher);
void makeNotif(string title, string text) {
manager.Notify("Download", notificationID, notif
.SetContentTitle(title)
.SetContentText(text)
.Build()
);
}
var rwls = new ReaderWriterLockSlim();
double progress = 0;
try {
var client = new YoutubeClient();
var video = await client.Videos.GetAsync(videoId);
makeNotif(video.Title, "Downloading");
var audioStream = (await client.Videos.Streams.GetManifestAsync(videoId)).GetAudioOnlyStreams().Where(info => info.Container == Container.Mp4).GetWithHighestBitrate();
notif.SetContentTitle(video.Title);
if (audioStream != null) {
string fileName = Path.Combine(
Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath,
SanitizeFilename(video.Title) + ".mp3"
);
Task notifLoop = Task.Run(async () => {
while (true) {
try {
rwls.EnterReadLock();
if (progress == -1) {
return;
} else if (progress >= 1) {
notif.SetProgress(0, 0, false);
notif.SetOngoing(false);
makeNotif(video.Title, "Finished downloading");
return;
} else {
notif.SetProgress(100, (int) (progress * 100), false);
manager.Notify("Download", notificationID, notif.Build());
notif.SetOngoing(true);
}
} finally {
rwls.ExitReadLock();
}
await Task.Delay(TimeSpan.FromMilliseconds(500));
}
});
await client.Videos.Streams.DownloadAsync(audioStream, fileName, new Progress<double>(p => {
try {
rwls.EnterWriteLock();
if (progress != -1) {
progress = p;
}
} finally {
rwls.ExitWriteLock();
}
}));
rwls.EnterWriteLock();
progress = 1;
rwls.ExitWriteLock();
await notifLoop;
} else {
makeNotif(video.Title, "This video cannot be downloaded. A future update may fix this.");
}
} catch (Exception e) {
rwls.EnterWriteLock();
Log.Error(LogTag, Java.Lang.Throwable.FromException(e), "Exception when trying to download video " + videoId.Value);
notif.SetProgress(0, 0, false);
notif.SetOngoing(false);
progress = -1;
makeNotif(e.GetType().Name, "Cannot download video because of an unknown error. Trying again may fix the problem. If this persists, contact the developer, and include a link to the video you downloaded.");
rwls.ExitWriteLock();
}
});
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) {
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
DoDownload();
}
private string SanitizeFilename(string inputStr) {
char[] input = inputStr.ToCharArray();
char[] invalidChars = Path.GetInvalidFileNameChars();
for (int i = 0; i < input.Length; i++) {
if (invalidChars.Contains(input[i])) {
input[i] = '_';
}
}
return new string(input);
}
}
}