Merge pull request #9 from hansputera/dev

feat(provider): add tokup.app site
This commit is contained in:
Hanif Dwy Putra S
2021-12-20 12:18:12 +07:00
committed by GitHub
7 changed files with 106 additions and 89 deletions

View File

@@ -37,11 +37,11 @@ Available on [Wiki](https://github.com/hansputera/tiktok-dl/wiki/Endpoints)
- [TTDownloader](https://ttdownloader.com)
- [Musically Down](https://musicaldown.com)
- [DLTik](https://dltik.com/)
- [TTSave](https://ttsave.app)
- [FireTik](https://firetik.com)
- [TTSave](https://ttsave.app) (Won't work)
- [DDDTik](https://dddtik.com)
- [TikDown](https://tikdown.org)
- [DownTik](https://downtik.net)
- [LoveTik](https://lovetik.com)
- [Tokup](https://tokup.app) (TTSave alternative)
Contribution(s) are welcome!

View File

@@ -11,7 +11,8 @@ export default async (req: VercelRequest, res: VercelResponse) => {
try {
ow(req.query, ow.object.partialShape({
'url': ow.string.url.validate((v) => ({
'validator': /(http|https):\/\/(.*)\.tiktok\.com\/(.*)/gi.test(v),
'validator': /^http(s?)(:\/\/)([a-z]+\.)*tiktok\.com\/(.*)?\/(.*)?$/gi
.test(v),
'message': 'Expected (.*).tiktok.com',
})),
'type': ow.optional.string.validate((v) => ({

View File

@@ -1,52 +0,0 @@
import {BaseProvider, ExtractedInfo} from './baseProvider';
import {getFetch} from '..';
import {handleException} from '../decorators';
import {matchLink} from './util';
/**
* @class FireTikProvider
*/
export class FireTikProvider extends BaseProvider {
/**
* Get resource name
*
* @return {string}
*/
public resourceName(): string {
return 'firetik';
}
public client = getFetch('https://firetik.com');
/**
* @param {string} url
* @return {Promise<ExtractedInfo>}
*/
@handleException
async fetch(url: string): Promise<ExtractedInfo> {
const response = await this.client.post('./down.php', {
'form': {
'url': url,
},
});
return this.extract(response.body);
}
/**
* @param {string} html
* @return {ExtractedInfo}
*/
extract(html: string): ExtractedInfo {
const urls = matchLink(html) as string[];
urls.pop();
const t = urls[1];
return {
'result': {
'urls': urls.filter((u) => u !== t),
'thumb': t,
},
};
}
}

View File

@@ -8,26 +8,26 @@ import {TTSave} from './ttSaveProvider';
import {DLTikProvider} from './DLTikProvider';
import {SaveFromProvider} from './saveFromProvider';
import {SaveTikProvider} from './saveTikProvider';
import {FireTikProvider} from './fireTikProvider';
import {TikDownProvider} from './tikDownProvider';
import {DownTikProvider} from './downTikProvider';
import {LoveTikProvider} from './loveTikProvider';
import {DDDTikProvider} from './dddTikProvider';
import {TokupProvider} from './tokupProvider';
export const Providers: BaseProvider[] = [
new SnaptikProvider(),
new TikmateProvider(),
new MusicalyDown(),
new TTDownloader(),
new TTSave(),
new TTSave(), // won't work because we coudn't receive the cookie.
new DLTikProvider(),
new SaveFromProvider(),
new SaveTikProvider(),
new FireTikProvider(), // DDDTik Mirror
new TikDownProvider(),
new DownTikProvider(), // SaveTik Mirror
new LoveTikProvider(),
new DDDTikProvider(),
new TokupProvider(), // ttsave alternative
];
export const getRandomProvider = () => Providers[

View File

@@ -0,0 +1,88 @@
import {BaseProvider, ExtractedInfo} from './baseProvider';
import {getFetch} from '../fetch';
/**
* @class TokupProvider
*/
export class TokupProvider extends BaseProvider {
/**
* Get provider name
* @return {string}
*/
public resourceName(): string {
return 'tokup';
}
public client = getFetch('https://tokup.app');
/**
* Fetch tokup
* @param {string} url - TikTok Video URL
* @return {Promise<ExtractedInfo>}
*/
public async fetch(
url: string,
): Promise<ExtractedInfo> {
const response = await this.client.post(
'./', {
'form': {
'url': url,
},
'headers': {
'Origin': this.client.defaults.options.prefixUrl,
'Referer': this.client.defaults.options.prefixUrl,
},
},
);
if (response.statusCode !== 200 ||
/video not found\b/gi.test(
response.body,
)) {
return {
'error': 'Video Not Found',
};
} else {
return this.extract(
response.body,
);
}
}
/**
* Extract tokup html elements
* @param {string} html
* @return {ExtractedInfo}
*/
extract(html: string): ExtractedInfo {
const authorProfile = (/http(s)?(:\/\/(.*)\.tiktokcdn\.com\/(.*))/gi.exec(
html,
) as string[])[0];
const nums = (html.match(/<td>[0-9]+<\/td>/g) as string[]).map(
(n) => n.replace(/<(\/)?[a-zA-Z0-9]+>/gi, ''));
const url = [...new Set(
(html.match(
// eslint-disable-next-line max-len
/http(s)?(:\/\/tikmate\.app\/download\/[A-Za-z0-9\-\_]+\/[0-9]+\.mp4+)/gi) as string[]),
)][0];
return {
'result': {
'urls': [url, url + '?hd=1'],
'advanced': {
'authorThumb': authorProfile
.substring(0, authorProfile.length-1),
'author': (/target="_blank"\>(.*)\</.exec(
html,
) as string[])[1],
'uploadedAt': (html.match(
/<p>(.+)<\/p>/,
) as string[])[1],
'likesCount': nums[0],
'commentsCount': nums[1],
'sharesCount': nums[2],
},
},
};
}
}

View File

@@ -3,6 +3,7 @@ import {handleException} from '../decorators';
import {BaseProvider, ExtractedInfo} from './baseProvider';
import {keyGeneratorTTSave, matchLink} from './util';
/**
* @class TTSave
*/
@@ -25,20 +26,23 @@ export class TTSave extends BaseProvider {
public async fetch(url: string): Promise<ExtractedInfo> {
// getting token
const response = await this.client('./');
const token = (
response.body.match(/m\(e,(.)?"(.*)"\)/) as string[]
).filter((x) => x)[1].split(/"\)}/)[0];
response.body.match(/(m|doDownload)?\(e,"(.*)"\)}/) as string[]
).filter((x) => x.length).pop() as string;
const key = await keyGeneratorTTSave(token);
const dlResponse = await this.client.post('./download.php', {
'json': {
'id': url,
'token': token,
'key': await keyGeneratorTTSave(token),
'key': key,
},
'headers': {
'Origin': this.client.defaults.options.prefixUrl,
'Referer': this.client.defaults.options.prefixUrl,
'Cookie': response.headers['set-cookie']?.toString(),
'Cookie': response.headers['set-cookie']?.toString(), // no cookies :(
...response.headers,
},
});

View File

@@ -1,7 +1,3 @@
import {NodeVM} from 'vm2';
import got from 'got';
import {client as redisClient} from '../../redis';
/**
* Generate key for ttsave.app
*
@@ -9,28 +5,8 @@ import {client as redisClient} from '../../redis';
* @return {string | undefined}
*/
export const keyGeneratorTTSave = async (token: string): Promise<string> => {
const vm = new NodeVM({
'compiler': 'javascript',
'console': 'inherit',
'require': {
'external': true,
'root': './',
},
});
const expectedLen = (token.length / 3);
let scriptCache = await redisClient.get('ttsave-scripts');
if (!scriptCache) {
const response = await got('https://gist.githubusercontent.com/hansputera/' +
'70e33b8ac4a9beca4725c95cf517e8a6' +
'/raw/9b1bf7f0eee16a69b38e2' +
'03782bd46dae1611f8e/ttsave-gen.js', {
dnsCache: true,
});
await redisClient.set('ttsave-scripts', response.body, 'ex', 60 * 10);
scriptCache = response.body;
}
return vm.run(scriptCache + 'module.exports = ' +
'key(`' + token + '`);', 'generator-ttsave.js');
return token.split('').reverse().join('')
.slice(-(expectedLen));
};