Package: degooged-tube
degooged-tubeAn adless, accountless, trackerless youtube interface with a sub boxINSTALLATIONABOUTCLI-USAGEAPI-USAGEDEVELOPMENTSCRAPINGSANATIZATIONYOUTUBE-API-WRAPPERSDATA-PIPELINEINSTALLATIONRequires mpv for terminal playbackInstall degooged-tube via pypi using pip:pip install degooged-tubeABOUTAllows for youtube to be used from a terminal with no ads, without an account account, and while maintaining subbox functionality.Can be embedded into other projects using the API indegooged_tube/ytApiHacking/__init__.pyAll youtube API scraping is done internally, with the exception of getting the streaming link for videos, which is done withyt-dlpCLI-USAGELaunch in terminal with commanddegooged-tubeFollow prompts to create a new 'user' (info is stored locally, has nothing to do with a youtube account), I recommend adding a few subs initially as it makes getting use to the cli easier.General InterfaceCLI is interactive, options are displayed on the bottom of the screen and can be selected by entering the letter in the brackets, IE w for(w)atch.Some options IE(w)atchwill show you a numbered list then prompt you for which number you want to watch, others IE(p)revious/(n)ext pagewill just preform the action.API-USAGEdegooged_tube/ytApiHacking/__init__.pycontains getter functions to return dataclasses, aswell asYtapiListof dataclasses containing scraped data, the data in the dataclasses is self explainitory and theYtapiListcan be indexed like normal python lists, throwingIndexErrorif value is out of bounds. There is alsoYtApiList.getPaginated(pageNum, pageSize)which returns a list of elements (pageSize is upper bound)DEVELOPMENTTo build for development run:git clone https://github.com/PrinceOfPuppers/degooged-tube

cd degooged-tube

pip install -e .This will build and install sync-dl in place, allowing you to work on the code without having to reinstall after changes.Automated Testingpython test.py [options]Will run all unit and integration tests, options are -u and -i to only run the unit/integration tests respectively.SCRAPINGyoutube will sometimes change their API, most of the time this is not an issue as the scraping engine is robust, however sometimes scrapers will be broken, as such here is a guide on how they work and how to repair themThe configuration of youtube-api hacking exists indegooged_tube/ytApiHacking/controlPanel.pyScrapeJsonTreeThe core of degooged-tube is a json scraping engine which is robust against changing apis, and easily repairable in the event of a scraper breaking.It works by defining nodes in a json tree down to the data you wish to collect, you can specify as many or as few nodes as is needed.Examples"greetings":{
    "hello": "there",
    "howdy": {"name": "partner"},
    "hi":[
        { 
            "name": "alice",
            "favColor": "blue",
            "coolness": "megaRad"
        },
        { 
            "name": "bob", 
            "favColor": "green",
            "coolness": 11
        },
        { 
            "name": "carol",
            "favColor": "purple",
            "coolness": ["super", "duper", "cool"]
        },
        { 
            "name": "dave",
            "favColor": "purple"
        }
    ]
}Example Scrapers:ScrapeNth("howdy")(N defaults to 1 or first)-> we would just get{"howdy": {"name": "partner"}}ScrapeNth("howdy", collapse = True)-> we would just get{"name": "partner"}, collapse just returns the data for a node, rather than key value pairsScrapeAll("name")-> we would just get{"name": ["partner", "alice", "bob", "carol", "dave"]}ScrapeNth("hi", [ScrapeNth("name" )])-> we would get{"hi": {"name": "alice"}}"because we would first get"hi", and then scrape for the first"name"in said dataScrapeNth("hi", [ScrapeNth("name", collapse=True)])-> we would get{"hi": "alice"}","name"has been collapsedScrapeNth("hi", [ScrapeNth("name"), ScrapeNth("favColor")])-> we would get{"hi": [{"name": "alice"}, {"favColor":"blue"}]}ScrapeAll("name", collapse=True, dataCondition=lambda data: data[1] = 'a')-> we would get['carol', 'dave']asdataConditionfilters all data which fail the conditionScrapeUnion([ScrapeNth("missing key",[], collapse=True), ScrapeNth("hello",[], collapse = True)])-> we would get"there", however if"missing key"was present we would get the data for that insteadFor more examples using all nodes and arguments, seedegooged_tube/ytApiHacking/controlPanel.py.For more involved examples of how this system is used in practice, seedegooged_tube/tests/unitTests.py.Node TypesScrapeNth:Scrapes and returns the data for n'th occurrence of the keyScrapeAll:Scrapes and returns the a list of data for all occurrence of the keyScrapeLongest:Scrapes and returns the data with largestlen(data)for all occurrence of the keyScrapeUnion([Node1, Node2, ...]):Will scrape for each node and return the data first one which matchesScrapeAllUnion/ScrapeAllUnionNode:Used together as such:ScrapeAllUnion([ScrapeAllUnionNode, ScrapeAllUnionNode, ...]), will return a list containing all matches for any of theScrapeAllUnionNodeScrapeElement:The generic type of any scrape nodeSANATIZATIONAfter scraping the data is passed into the.fromData()constructor of a dataclass (ieVideoInfo.fromData()), all of which are located indegooged_tube/ytApiHacking/controlPanel.pywhere it is sanitized and any missing data can be filled with placeholder dataYOUTUBE-API-WRAPPERSThere are 3 Objects which which wrap the youtube-api and allow for easy access to resourcesYtInitalPageConstructed usingYtInitalPage.fromUrl(url: str)gets a page and parses it for continuation chains and initial dataThe methodpage.scrapeInitalData(dataFmt: Union[ScrapeElement, list[ScrapeElement]])will run scrapeJsonTree on any initial data found and return it as a dictYtApiListConstructed fromYtInitalPage, it behaves like a list and making calls to a continuation chain as items are indexed.The continuation chain is decided using the apiUrl, and by duck-typing based on the scrapeFmt (following only chains which match the dataFmt).ContainsYtContIter, an iterator which wraps the continuation chains, deals the requests, as well as implements the aforementioned duck-typingDATA-PIPELINEpipeline under the hood (note this is not a callstack, for example processInfo is called in getter, its more a sequence diagram):youtube-api
                                                                    ||
url/query -------------> getter --> YtInitalPage --+     +--> scrapeInitalData -------> scrapeJsonTree --> processInfo ---+---> fromData --> dataclass
   |                                               +-----+                                                                |
   +--> YtInitalPage --> getter -------------------+     +--> YtApiList/YtContIter ---> scrapeJsonTree --> onExtend ------+
                                                                    ||
                                                                youtube-apiLegend:url/query:url or search queryYtInitalPage:created usingfromUrl()getter:functions indegooged_tube/ytApiHacking/__init__.pyprepended bygetscrapeInitalData:a method ofYtInitalPageis passes the requisite format to by getterYtApiList\YtContIter:YtApiListis instantiated by getter and passes the requisite format to by getter,YtContIterin instantiated as a component ofYtApiListscrapeJsonTree:uses the format on data requested from the youtube-apiprocessInfo:the functions indegooged_tube/ytApiHacking/__init__.pycalledprocess[BLANK]Info, typically just callsfromDataon the requisite dataclassonExtend:the functions indegooged_tube/ytApiHacking/__init__.pycalled[BLANK]OnExtend, typically just callsfromDataon the requisite dataclass (can be overridden to inject data usingOnExtendKwargsin a few cases)fromData:the constructors indegooged_tube/ytApiHacking/controlPanel.pywhich create a dataclass to hold the scraped data (ieVideoInfo.fromData()). they also sanitize and any replaces missing fields with placeholder data if possible