Skip to content
This repository was archived by the owner on Mar 3, 2024. It is now read-only.

Commit 3d6f6df

Browse files
committed
Merge branch 'feature/9a_server'
2 parents d134c0b + ac94dec commit 3d6f6df

File tree

3 files changed

+198
-42
lines changed

3 files changed

+198
-42
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
33
[![Build Status](https://travis-ci.org/lap00zza/Grabber.svg?branch=master)](https://travis-ci.org/lap00zza/Grabber)
44

5-
Grab everything!
6-
5+
Grab everything! Currently support grabbing videos from **9anime** (ex: Server 4) and **RapidVideo**
6+
![](https://image.ibb.co/cF7iEa/Grabber.png)
77
## Download
88
* [Greasyfork](https://greasyfork.org/en/scripts/31010-grabber)
99

@@ -14,7 +14,7 @@ Grab everything!
1414
4. The Grab All button will appear near RapidVideo. Click it!
1515
5. Make sure to keep a download manager handy. The grabbed links will be copied to your clipboard.
1616

17-
**If you get a Tampermonkey page requesting origin permission, remember to click allow.**
17+
:warning: **If you get a Tampermonkey page requesting origin permission, remember to click allow.**
1818

1919
## What is metadata.json?
2020
> You can use [Renamer](https://github.com/lap00zza/Renamer) to bulk rename files using metadata.json

grabber.user.js

+194-38
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ==UserScript==
22
// @name Grabber
33
// @namespace https://github.com/lap00zza/
4-
// @version 0.5.1
4+
// @version 0.6.0
55
// @description Grab links from 9anime!
66
// @author Jewel Mahanta
77
// @icon https://image.ibb.co/fnOY7k/icon48.png
@@ -18,10 +18,14 @@
1818

1919
(function () {
2020
'use strict'
21+
// TODO: convert anime names to Title Case
22+
// TODO: add a option to select quality for 9anime server.
23+
// right now it just grabs everything.
24+
2125
console.log('Grabber ' + GM_info.script.version + ' is now running!')
2226
var dlInProgress = false // global switch to indicate dl status
2327
var dlEpisodeIds = [] // list of id's currently being grabbed
24-
var dlServerType = '' // FIXME: cant queue different server types together
28+
var dlServerType = ''
2529
var dlAggregateLinks = '' // stores all the grabbed links as a single string
2630
var ts = document.getElementsByTagName('body')[0].dataset['ts'] // ts is needed to send API requests
2731
var animeName = document.querySelectorAll('h1.title')[0].innerText
@@ -114,17 +118,19 @@
114118
}
115119

116120
/********************************************************************************************************************/
121+
// Utility functions
117122
/**
118123
* Just as the function name says!
119-
* We replace the illegal characters with underscore (_)
124+
* We remove the illegal characters.
120125
* @param filename
121126
* @returns {string}
122127
*/
123128
function generateFileSafeString (filename) {
124129
var re = /[\\/<>*?:"|]/gi
125-
return filename.replace(re, '_')
130+
return filename.replace(re, '')
126131
}
127132

133+
// metadataUrl is a part of createMetadataFile
128134
var metadataUrl = null
129135
/**
130136
* This functions generates the blob for the `metadata.json`
@@ -157,6 +163,87 @@
157163
}
158164
}
159165

166+
/**
167+
* Generate the query parameters from an object.
168+
* @param {object} params
169+
* @returns {string}
170+
*/
171+
function generateQParams (params) {
172+
var qParams = ''
173+
var dKeys = Object.keys(params)
174+
for (var i = 0; i < dKeys.length; i++) {
175+
if (i === 0) {
176+
qParams += dKeys[i] + '=' + params[dKeys[i]]
177+
} else {
178+
qParams += '&' + dKeys[i] + '=' + params[dKeys[i]]
179+
}
180+
}
181+
return qParams
182+
}
183+
184+
// parser is a part of getURL
185+
var parser = document.createElement('a')
186+
/**
187+
* Get a url from a uri string.
188+
* Credits to jlong for this implementation idea:
189+
* https://gist.github.com/jlong/2428561
190+
* @param {string} uriString
191+
* @returns {string}
192+
*/
193+
function getURL (uriString) {
194+
parser.href = uriString
195+
return parser.protocol + '//' + parser.hostname + parser.pathname
196+
}
197+
198+
/**
199+
* Converts the searchParams in then uri string to
200+
* an object.
201+
* @param {string} uriString
202+
* @returns {object}
203+
*/
204+
function searchParams2Obj (uriString) {
205+
parser.href = uriString
206+
// HTMLHyperlinkElementUtils.search returns a search
207+
// string, also called a query string containing a '?'
208+
// followed by the parameters of the URL. We don't need
209+
// the '?' so we slice it.
210+
var searchParams = parser.search.slice(1)
211+
// All search params are delimited by '&'.
212+
// So we split them into an array and iterate
213+
// through it to get the keys and values.
214+
var search = searchParams.split('&')
215+
var searchObj = {}
216+
for (var i = 0; i < search.length; i++) {
217+
var searchSplit = search[i].split('=')
218+
if (searchSplit[0] !== '' && searchSplit[1] !== undefined) {
219+
searchObj[searchSplit[0]] = searchSplit[1]
220+
}
221+
}
222+
return searchObj
223+
}
224+
225+
/**
226+
* A simple helper function that merges 2 objects.
227+
* @param {object} obj1
228+
* @param {object} obj2
229+
* @returns {object}
230+
*/
231+
function mergeObject (obj1, obj2) {
232+
var obj3 = {}
233+
for (var a in obj1) {
234+
if (obj1.hasOwnProperty(a)) {
235+
obj3[a] = obj1[a]
236+
}
237+
}
238+
for (var b in obj2) {
239+
if (obj2.hasOwnProperty(b)) {
240+
obj3[b] = obj2[b]
241+
}
242+
}
243+
return obj3
244+
}
245+
246+
/********************************************************************************************************************/
160247
/**
161248
* Generates the name of the original mp4 file (RapidVideo).
162249
* @param url
@@ -214,16 +301,52 @@
214301
})
215302
}
216303

304+
/**
305+
* Fetch 9anime video links for Server F4 etc.
306+
* @param {string} url
307+
* The 9anime url to grab videos
308+
* @param {object} params
309+
* A list of query parameters to send to the API.
310+
* @returns {Promise}
311+
*/
312+
function getVideoLinks9a (url, params) {
313+
return new Promise(function (resolve, reject) {
314+
var xhr = new window.XMLHttpRequest()
315+
xhr.open('GET', url + '?' + generateQParams(params), true)
316+
xhr.onload = function () {
317+
// Some error codes don't trigger the
318+
// onerror. So we make sure that we only
319+
// parse the response text for 200.
320+
if (xhr.status === 200) {
321+
try {
322+
resolve(JSON.parse(xhr.responseText))
323+
} catch (e) {
324+
// This is when there is an error
325+
// parsing the response text
326+
reject(e)
327+
}
328+
} else {
329+
reject(xhr.statusText)
330+
}
331+
}
332+
xhr.onerror = function () {
333+
console.log('error')
334+
reject(xhr.statusText)
335+
}
336+
xhr.send()
337+
})
338+
}
339+
217340
/**
218341
* Get the grabber info from the 9anime API.
219-
* @param {string} qParams
342+
* @param {object} params
220343
* A list of query parameters to send to the API.
221344
* @returns {Promise}
222345
*/
223-
function getGrabber (qParams) {
346+
function getGrabber (params) {
224347
return new Promise(function (resolve, reject) {
225348
var xhr = new window.XMLHttpRequest()
226-
xhr.open('GET', '/ajax/episode/info?' + qParams, true)
349+
xhr.open('GET', '/ajax/episode/info' + '?' + generateQParams(params), true)
227350
xhr.onload = function () {
228351
// Some error codes don't trigger the
229352
// onerror. So we make sure that we only
@@ -290,42 +413,72 @@
290413
var ep = dlEpisodeIds.shift()
291414
grabberStatus.innerHTML = 'Fetching ' + ep.num
292415

293-
var data = {
416+
var params = {
294417
ts: ts,
295418
id: ep.id,
296419
update: 0
297420
}
298-
data['_'] = generateToken(data)
421+
params['_'] = generateToken(params)
299422

300-
// Generate the query parameters
301-
var qParams = ''
302-
var dKeys = Object.keys(data)
303-
for (var i = 0; i < dKeys.length; i++) {
304-
if (i === 0) {
305-
qParams += dKeys[i] + '=' + data[dKeys[i]]
306-
} else {
307-
qParams += '&' + dKeys[i] + '=' + data[dKeys[i]]
308-
}
309-
}
310-
getGrabber(qParams)
423+
getGrabber(params)
311424
.then(function (resp) {
312-
if (dlServerType === 'RapidVideo') {
313-
getVideoLinksRV(resp['target'])
314-
.then(function (resp) {
315-
dlAggregateLinks += resp[0]['file'] + '\n'
316-
var fileSafeName = generateFileSafeString(animeName + '-ep_' + ep.num + '-' + resp[0]['label']) + '.mp4'
317-
// Metadata only for RapidVideo
318-
metadata.files.push({
319-
original: generateRVOriginal(resp[0]['file']),
320-
real: fileSafeName.toLowerCase()
425+
switch (dlServerType) {
426+
case 'RapidVideo':
427+
getVideoLinksRV(resp['target'])
428+
.then(function (resp) {
429+
dlAggregateLinks += resp[0]['file'] + '\n'
430+
var fileSafeName = generateFileSafeString(animeName + '-ep_' + ep.num + '-' + resp[0]['label']) + '.mp4'
431+
// Metadata only for RapidVideo
432+
metadata.files.push({
433+
original: generateRVOriginal(resp[0]['file']),
434+
real: fileSafeName.toLowerCase()
435+
})
436+
grabberStatus.innerHTML = 'Completed ' + ep.num
437+
requeue()
438+
})
439+
.catch(function (e) {
440+
console.debug(e)
441+
grabberStatus.innerHTML = '<span class="grabber--fail">Failed ' + ep.num + '</span>'
442+
requeue()
443+
})
444+
break
445+
446+
case '9anime':
447+
var data = {
448+
ts: ts,
449+
id: resp['params']['id'],
450+
options: resp['params']['options'],
451+
token: resp['params']['token'],
452+
mobile: 0
453+
}
454+
var url = getURL(resp['grabber'])
455+
// The grabber url has additional search params
456+
// we need to add those to 'data' before generating
457+
// the token.
458+
var sParams = searchParams2Obj(resp['grabber'])
459+
var merged = mergeObject(data, sParams)
460+
var initState = s(a(DD + url, ''))
461+
merged['_'] = generateToken(merged, initState)
462+
getVideoLinks9a(url, merged)
463+
.then(function (resp) {
464+
// resp is of the format
465+
// {data: [{file: '', label: '', type: ''}], error: null, token: ''}
466+
// data contains the files array.
467+
var data = resp['data']
468+
for (var i = 0; i < data.length; i++) {
469+
var title = generateFileSafeString(animeName + '-ep_' + ep.num + '-' + data[i]['label'])
470+
dlAggregateLinks += data[i]['file'] + '?&title=' + title.toLowerCase() +
471+
'&type=video/' + data[i]['type'] + '\n'
472+
}
473+
grabberStatus.innerHTML = 'Completed ' + ep.num
474+
requeue()
475+
})
476+
.catch(function (e) {
477+
console.debug(e)
478+
grabberStatus.innerHTML = '<span class="grabber--fail">Failed ' + ep.num + '</span>'
479+
requeue()
321480
})
322-
grabberStatus.innerHTML = 'Completed ' + ep.num
323-
requeue()
324-
})
325-
.catch(function () {
326-
grabberStatus.innerHTML = '<span class="grabber--fail">Failed ' + ep.num + '</span>'
327-
requeue()
328-
})
481+
break
329482
}
330483
})
331484
.catch(function () {
@@ -376,10 +529,13 @@
376529
// Attach the 'Grab All' button to RapidVideo for now.
377530
var serverLabels = document.querySelectorAll('.server.row > label')
378531
for (var i = 0; i < serverLabels.length; i++) {
532+
// Remove the leading and trailing whitespace
533+
// from the server labels.
379534
var serverLabel = serverLabels[i].innerText.trim()
380-
if (/^RapidVideo$/i.test(serverLabel)) {
381-
console.log(serverLabels[i])
535+
if (/RapidVideo/i.test(serverLabel)) {
382536
serverLabels[i].appendChild(generateDlBtn('RapidVideo'))
537+
} else if (/Server\s+F/i.test(serverLabel)) {
538+
serverLabels[i].appendChild(generateDlBtn('9anime'))
383539
}
384540
}
385541
})()

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "grabber",
3-
"version": "0.5.1",
3+
"version": "0.6.0",
44
"description": "Grab video links from 9anime",
55
"main": "grabber.user.js",
66
"scripts": {

0 commit comments

Comments
 (0)