mirror of
https://github.com/hansputera/tiktok-dl.git
synced 2026-04-05 19:51:57 +02:00
Merge pull request #9 from hansputera/dev
feat(provider): add tokup.app site
This commit is contained in:
@@ -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!
|
||||
|
||||
@@ -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) => ({
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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[
|
||||
|
||||
88
lib/providers/tokupProvider.ts
Normal file
88
lib/providers/tokupProvider.ts
Normal 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],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user