Dinoosauro / tiktok-to-ytdlp

Fetch all the liked videos, videos from an user, videos with a specific sound etc. from TikTok, and creates a script to download them with yt-dlp
MIT License
172 stars 16 forks source link

[Feature Request] - Make this into a chrome extension #18

Closed jeffscottward closed 1 month ago

jeffscottward commented 3 months ago

Would be nice to have a UI to adjust parameters, and to not have to copy paste code and manually manipulate inputs.

I will come back to this and do it myself in about 2 months unless you have the time.

jeffscottward commented 3 months ago

I put it into ChatGPT to an ASCII mockup.

+---------------------------------------------------------+
|                                                         |
|    TikTok to yt-dlp Configuration Panel                 |
|                                                         |
+---------------------------------------------------------+
|                                                         |
|    Scrolling Time (ms)                                  |
|    ----------------------                               |
|    Min Time: [    1000   ]                              |
|    Max Time: [    3000   ]                              |
|                                                         |
|    Minimum Views: [      0  ]                           |
|                                                         |
|    Delete from next txt?                                |
|    ----------------------                               |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Output Name Type                                     |
|    ----------------------                               |
|    ( ) Generic String                                   |
|    (0) Fetch from data tags                             |
|    (1) Webpage Title                                    |
|    (2) First Title on Page (H1)                         |
|    ( ) Default (TikTokLinks.txt)                        |
|                                                         |
|    Adapt Text Output?                                   |
|    ----------------------                               |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Allow Images?                                        |
|    ----------------------                               |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Advanced Options                                     |
|    ----------------------                               |
|    Get Array After Scroll?                              |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Get Link by Filter?                                  |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Check Nullish Link?                                  |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Log Link Error?                                      |
|    [X] Yes    [ ] No                                    |
|                                                         |
|    Maximum Downloads: [     100  ]                      |
|                                                         |
|    ------------------------------------------------     |
|                                                         |
|    [  Save  ]       [  Reset  ]      [  Exit  ]         |
|                                                         |
+---------------------------------------------------------+
jeffscottward commented 3 months ago

Here is a boilerplate - https://github.com/lxieyang/chrome-extension-boilerplate-react

jeffscottward commented 3 months ago

And here is some code CHATGPT made

import React, { useState } from 'react';

const TikTokToYtdlpConfigPanel: React.FC = () => {
    const [minTime, setMinTime] = useState<number>(1000);
    const [maxTime, setMaxTime] = useState<number>(3000);
    const [minViews, setMinViews] = useState<number>(0);
    const [deleteFromNextTxt, setDeleteFromNextTxt] = useState<boolean>(true);
    const [outputNameType, setOutputNameType] = useState<string>('2');
    const [adaptTextOutput, setAdaptTextOutput] = useState<boolean>(true);
    const [allowImages, setAllowImages] = useState<boolean>(true);
    const [getArrayAfterScroll, setGetArrayAfterScroll] = useState<boolean>(true);
    const [getLinkByFilter, setGetLinkByFilter] = useState<boolean>(true);
    const [checkNullishLink, setCheckNullishLink] = useState<boolean>(true);
    const [logLinkError, setLogLinkError] = useState<boolean>(true);
    const [maximumDownloads, setMaximumDownloads] = useState<number>(100);

    const handleSave = () => {
        // Logic to save the configuration
        console.log('Configuration saved');
    };

    const handleReset = () => {
        // Logic to reset the configuration to default values
        setMinTime(1000);
        setMaxTime(3000);
        setMinViews(0);
        setDeleteFromNextTxt(true);
        setOutputNameType('2');
        setAdaptTextOutput(true);
        setAllowImages(true);
        setGetArrayAfterScroll(true);
        setGetLinkByFilter(true);
        setCheckNullishLink(true);
        setLogLinkError(true);
        setMaximumDownloads(100);
        console.log('Configuration reset to default');
    };

    const handleExit = () => {
        // Logic to close the panel
        console.log('Exit configuration panel');
    };

    return (
        <div style={{ padding: '20px', border: '1px solid black', width: '400px' }}>
            <h2>TikTok to yt-dlp Configuration Panel</h2>

            <div>
                <label>Scrolling Time (ms)</label>
                <div>
                    <label>Min Time:</label>
                    <input type="number" value={minTime} onChange={(e) => setMinTime(Number(e.target.value))} />
                </div>
                <div>
                    <label>Max Time:</label>
                    <input type="number" value={maxTime} onChange={(e) => setMaxTime(Number(e.target.value))} />
                </div>
            </div>

            <div>
                <label>Minimum Views:</label>
                <input type="number" value={minViews} onChange={(e) => setMinViews(Number(e.target.value))} />
            </div>

            <div>
                <label>Delete from next txt?</label>
                <div>
                    <input type="radio" id="deleteYes" name="deleteFromNextTxt" checked={deleteFromNextTxt} onChange={() => setDeleteFromNextTxt(true)} />
                    <label htmlFor="deleteYes">Yes</label>
                    <input type="radio" id="deleteNo" name="deleteFromNextTxt" checked={!deleteFromNextTxt} onChange={() => setDeleteFromNextTxt(false)} />
                    <label htmlFor="deleteNo">No</label>
                </div>
            </div>

            <div>
                <label>Output Name Type</label>
                <div>
                    <input type="radio" id="genericString" name="outputNameType" value="generic" checked={outputNameType === 'generic'} onChange={(e) => setOutputNameType(e.target.value)} />
                    <label htmlFor="genericString">Generic String</label>
                    <input type="radio" id="fetchFromDataTags" name="outputNameType" value="0" checked={outputNameType === '0'} onChange={(e) => setOutputNameType(e.target.value)} />
                    <label htmlFor="fetchFromDataTags">Fetch from data tags</label>
                    <input type="radio" id="webpageTitle" name="outputNameType" value="1" checked={outputNameType === '1'} onChange={(e) => setOutputNameType(e.target.value)} />
                    <label htmlFor="webpageTitle">Webpage Title</label>
                    <input type="radio" id="firstTitleOnPage" name="outputNameType" value="2" checked={outputNameType === '2'} onChange={(e) => setOutputNameType(e.target.value)} />
                    <label htmlFor="firstTitleOnPage">First Title on Page (H1)</label>
                    <input type="radio" id="defaultName" name="outputNameType" value="default" checked={outputNameType === 'default'} onChange={(e) => setOutputNameType(e.target.value)} />
                    <label htmlFor="defaultName">Default (TikTokLinks.txt)</label>
                </div>
            </div>

            <div>
                <label>Adapt Text Output?</label>
                <div>
                    <input type="radio" id="adaptYes" name="adaptTextOutput" checked={adaptTextOutput} onChange={() => setAdaptTextOutput(true)} />
                    <label htmlFor="adaptYes">Yes</label>
                    <input type="radio" id="adaptNo" name="adaptTextOutput" checked={!adaptTextOutput} onChange={() => setAdaptTextOutput(false)} />
                    <label htmlFor="adaptNo">No</label>
                </div>
            </div>

            <div>
                <label>Allow Images?</label>
                <div>
                    <input type="radio" id="imagesYes" name="allowImages" checked={allowImages} onChange={() => setAllowImages(true)} />
                    <label htmlFor="imagesYes">Yes</label>
                    <input type="radio" id="imagesNo" name="allowImages" checked={!allowImages} onChange={() => setAllowImages(false)} />
                    <label htmlFor="imagesNo">No</label>
                </div>
            </div>

            <div>
                <label>Advanced Options</label>
                <div>
                    <label>Get Array After Scroll?</label>
                    <input type="checkbox" checked={getArrayAfterScroll} onChange={() => setGetArrayAfterScroll(!getArrayAfterScroll)} />
                </div>
                <div>
                    <label>Get Link by Filter?</label>
                    <input type="checkbox" checked={getLinkByFilter} onChange={() => setGetLinkByFilter(!getLinkByFilter)} />
                </div>
                <div>
                    <label>Check Nullish Link?</label>
                    <input type="checkbox" checked={checkNullishLink} onChange={() => setCheckNullishLink(!checkNullishLink)} />
                </div>
                <div>
                    <label>Log Link Error?</label>
                    <input type="checkbox" checked={logLinkError} onChange={() => setLogLinkError(!logLinkError)} />
                </div>
            </div>

            <div>
                <label>Maximum Downloads:</label>
                <input type="number" value={maximumDownloads} onChange={(e) => setMaximumDownloads(Number(e.target.value))} />
            </div>

            <div style={{ marginTop: '20px' }}>
                <button onClick={handleSave}>Save</button>
                <button onClick={handleReset}>Reset</button>
                <button onClick={handleExit}>Exit</button>
            </div>
        </div>
    );
};

export default TikTokToYtdlpConfigPanel;
jeffscottward commented 3 months ago

I have too many things on my plate right now but good notes for later.

Dinoosauro commented 3 months ago

Hi! I've created an extension that permits to do that. I'll do some tests again this evening, but for now it seems to work. If you want to try it, see the instructions at https://github.com/Dinoosauro/tiktok-to-ytdlp/tree/browser-extension-and-views-fixes/extension If you find any issues, let me know and I'll try to fix them

jeffscottward commented 3 months ago

Very cool! Thanks! Will await your testing probably.

I need it to be actually released to the chrome store if possible. Want to try some experiments with Playwright.

On Tue, Jun 25, 2024 at 10:40 AM Dinoosauro @.***> wrote:

Hi! I've created an extension that permits to do that. I'll do some tests again this evening, but for now it seems to work. If you want to try it, see the instructions at https://github.com/Dinoosauro/tiktok-to-ytdlp/tree/browser-extension-and-views-fixes/extension If you find any issues, let me know and I'll try to fix them

— Reply to this email directly, view it on GitHub https://github.com/Dinoosauro/tiktok-to-ytdlp/issues/18#issuecomment-2189147530, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJUO7TMN3WGDQ5MO62JXELZJF6MPAVCNFSM6AAAAABJ2S5WPCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOBZGE2DONJTGA . You are receiving this because you authored the thread.Message ID: @.***>

jeffscottward commented 3 months ago

are you gonna release to the store so I can get auto updates, no more loading unpacked ?

Dinoosauro commented 3 months ago

I'll look into that. I ditched Chrome years ago, so I know next to nothing about their web extension store. However, if there's no fee, I'll try to upload it there. Otherwise, that might be a problem

jeffscottward commented 3 months ago

It’s totally free you’re good to go. 👍 Should be painless. I use chromium whenever possible, for obvious reasons these days so i understand.


Jeff Scott Ward Founder / A.I. & U.I. Engineer F0.ai

On Fri, Jun 28, 2024 at 10:42 AM Dinoosauro @.***> wrote:

I'll look into that. I ditched Chrome years ago, so I know next to nothing about their web extension store. However, if there's no fee, I'll try to upload it there. Otherwise, that might be a problem

— Reply to this email directly, view it on GitHub https://github.com/Dinoosauro/tiktok-to-ytdlp/issues/18#issuecomment-2197097685, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJUO7Q5GTR6O6Q5NAMEQ73ZJVY5FAVCNFSM6AAAAABJ2S5WPCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJXGA4TONRYGU . You are receiving this because you authored the thread.Message ID: @.***>

jeffscottward commented 2 months ago

I see you haven't uploaded it yet to the store, but also it ouputs the JSON and asks to download as .txt file.

also ytdlp doesn't understand the format? not clear how it injests JSON.

using this command yt-dlp -a TikTokLinks.txt -o "TikTok/%(uploader)s/%(upload_date)s-%(timestamp)s-%(id)s.%(ext)s" --trim-filenames 250 --write-pages --write-subs --write-auto-subs --write-description --write-info-json --write-playlist-metafiles

Renders this,

Screenshot 2024-07-05 at 9 54 56 AM
Dinoosauro commented 2 months ago

I've not uploaded it on the Chrome Web Store since unfortunately there's a fee to pay, I'll look if there are other options for Chromium-based browsers

As far as I know, yt-dlp does not support JSON files for the input. You need to convert that JSON in a list of URL, one for each line, without [] or "". If you don't want to re-run the script for everything, you can just do `JSON.parse(copy here the JSON file`).map(e => e.url).join("\n");` and then copy the result into theamericanincome.txt` file

jeffscottward commented 2 months ago

Ahh that stinks.

Thanks for the code! I actually just reran it with the same page open as they were all in the dom and it worked fine.

I guess just the default file type needs the conditional based on choice, then it’s good to go.

Thanks so much for this tool it’s going to help a lot!


Jeff Scott Ward Founder / A.I. & U.I. Engineer F0.ai

On Sat, Jul 6, 2024 at 3:34 AM Dinoosauro @.***> wrote:

I've not uploaded it on the Chrome Web Store since unfortunately there's a fee to pay, I'll look if there are other options for Chromium-based browsers

As far as I know, yt-dlp does not support JSON files for the input. You need to convert that JSON in a list of URL, one for each line, without [] or "". If you don't want to re-run the script for everything, you can just do JSON.parse(copy here the JSON file).map(e => e.url).join("\n"); and then copy the result into the americanincome.txt file

— Reply to this email directly, view it on GitHub https://github.com/Dinoosauro/tiktok-to-ytdlp/issues/18#issuecomment-2211693812, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJUO7QKMBDIOXHZLIKFYKLZK6MXNAVCNFSM6AAAAABJ2S5WPCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJRGY4TGOBRGI . You are receiving this because you authored the thread.Message ID: @.***>

Dinoosauro commented 2 months ago

Glad I helped! For the default file type, I'll try to update the script in a few minutes