{ "info": { "author": "Vladimir Belitskiy", "author_email": "belitskiy@gmail.com", "bugtrack_url": null, "classifiers": [ "Framework :: Flask", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython" ], "description": "## The goal of the app\nis to gather some data about your YouTube watch history (available via Google Takeout) and do some light visualization\nof it. There's a few built-in interactive graphs and tables, delivered via a web page, and then there's the \ndata itself that can be used for more. An SQLite browser, such as [DB Browser for SQLite](https://sqlitebrowser.org/),\ncould be used for viewing and filtering it like a spreadsheet, as well as making simple graphs. \n\nOutside of requests to YouTube Data API, the whole thing is run locally.\n\nThis is *not* a tool for exhaustive data gathering/archiving and records keeping. Even if it tried to be, inaccuracies\nin Takeout would not allow for [that](#takeout-quirks-and-data-accuracy).\n\n## What you'll need\n\nIn addition to **Python 3.6+** and installing the package (preferably in a \n[virtual environment](https://docs.python.org/3/library/venv.html)):\n\n```\npip install youtubewatched\n```\n\nyou'll need two things:\n - Your [Google Takeout](https://takeout.google.com/settings/takeout) YouTube data, in **English**. If yours isn't, switching your [language](https://myaccount.google.com/language?utm_source=google-account&utm_medium=web)\n to English should make the Takeout archives created afterwards be in English. \n - have YouTube Data API enabled and an **API key** for the app to make requests for information on \neach video. The first part from **Before you start** section from \n[Google's guide](https://developers.google.com/youtube/v3/getting-started) on the matter explains how to do that (should\n only be a few minutes):\n\n> 1. You need a Google Account to access the Google API Console, request an API key, and register your application.\n> 2. Create a project in the [Google Developers Console](https://console.developers.google.com/)\n and [obtain authorization credentials](https://developers.google.com/youtube/registering_an_application)\n so your application can submit API requests.\n> 3. After creating your project, make sure the YouTube Data API is one of the services that your application is \n> registered to use:\n>> a. Go to the [API Console](https://console.developers.google.com/) and select the project that you just registered. \n>> b. Visit the [Enabled APIs page](https://console.developers.google.com/apis/enabled). In the list of APIs, make\n>> sure the status is ON for the YouTube Data API v3.\n\n\\**the above block of text is a modification based on work created and shared by Google and used according to terms \ndescribed in the Creative Commons 3.0 Attribution License.*\\*\n\n## Running the app\n\nFrom your terminal, enter:\n```\nyoutubewatched\n```\nThat'll start up the app on `http://127.0.0.1:5000` (may take a few seconds). \nEnter `youtubewatched --help` for some limited server startup options.\n\nThe rest (there isn't much) is explained on the web page itself.\n\n#### Browser compatibility\n\nChrome, Firefox, Opera, Brave and hopefully Safari should all work fine as long as not terribly outdated; Edge and IE\nwill not.\n\n#### Possible issues\nOpening multiple instances of the front page will lead to wacky tracking of records' insertion or updating, though the \nprocess itself won't be affected. Close all, but one and maybe refresh that one.\n\nIf videos' graphs for 1k+ records show up blank, **WebGL** in your browser is probably disabled or otherwise prevented \nfrom working. \nIn Brave specifically, that could be fixed by clicking on the **Shields** icon in the address bar and \nallowing device recognition.\n\n## Notes on how the app works\n\n### Data retrieval and insertion process\n\nTakeout's watch-history.html file(s) gets parsed for the available info. Some records will only contain a timestamp of \nwhen the video was opened, presumably when the video itself is no longer available. Most will also contain the video ID,\n title and the channel title. \n\nAll the video IDs are then queried against YouTube Data API for additional information such as likes, tags, number of \ncomments, etc. Combined with the timestamps from Takeout, the records are then inserted into a database, located in the \nproject directory under the default name of yt.sqlite. Those without any identifying info are collectively inserted as a\n single 'unknown'.\n\nEach successful query to the API uses 11 points, with the standard daily quota varying wildly, depending on some factors.\nThe Quotas tab on Google's [Console](https://console.developers.google.com/apis/api/youtube.googleapis.com/overview)\npage will show how many have been used up.\n\nShould the process get interrupted for any reason, it's safe to restart it using the same Takeout files; no duplicates \nwill be created and no duplicate queries will be made (except one for updating the 'categories' table every time).\n\n### Takeout quirks and data accuracy\n\nTakeout works strangely. Only the last few years of watch history seem to ever get returned. \nIn addition to that, varying numbers of entries get returned each time an archive is created, with more \nrecent versions sometimes including older entries than the previous versions, as well as more entries throughout the \nwhole watch history. \n\nYouTube's History page keeps a more complete record, though, inversely, it also misses some entries that are present \nin Takeout. Most of those are for videos that are no longer available.\n\n#### Timestamps\n\nIn short, the timestamps can be very inaccurate and the app doesn't fix that. They shouldn't be relied on for anything\nprecise, but would work fine for a rough overview of activity over a given period of time, etc.\n\nThere is no timezone information coming from Takeout beyond abbreviations like EDT/PST/CET, some of which may refer to \nmultiple different timezones. The timestamps seem to be returned in local time of what's used to browse YouTube \n(or perhaps use Google products in general), including those for videos that were watched in a different timezone.\nTemporarily changing the timezone on the computer used to request the Takeout archive creation, or in Google \nCalendar, or the region in Google Search Settings, doesn't trigger a change in the timestamps.\n\nOne of the worse things happens with DST zones. In the case of zones observing Daylight Saving Time (DST), all of the\ntimestamps seem to be set to either the DST timezone or the non-DST one, depending on the date the archive was created.\nThat is, if someone who lives on the East coast of US were to create an archive in May, all the timestamps, including\nones that should be in EST (November - March) would be set to EDT, and vice versa if they were to create it in February.\n\n#### Avoiding duplicate timestamps because of potential different timezones for different Takeout archives\n\nSince different Takeout archives may have different timezones, depending on when/where they were downloaded, there may \nbe duplicate timestamps in different timezones. To weed out them out, any timestamps for the same video ID that have\nbeen watched at the same year, month, minute and second as well as less than 26 hours apart are treated as one. This may\n also block a very limited amount (likely less than a dozen for most) of legitimate timestamps from being entered. \n Most if not all of them would be the ones attached to the 'unknown' record.\n\n## Built with significant use of the following packages\n - [Flask](http://flask.pocoo.org/) - the app itself\n - [Dash](https://plot.ly/products/dash/) / Plotly - visualizing data and making interactive graphs, constructing the\n visualization web page\n - [Pandas](https://pandas.pydata.org/) & [NumPy](https://www.numpy.org/) - data wrangling\n - [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/) - parsing Google Takeout\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/VldmrB/youtube-watched", "keywords": "visualization youtube takeout", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "youtubewatched", "package_url": "https://pypi.org/project/youtubewatched/", "platform": "", "project_url": "https://pypi.org/project/youtubewatched/", "project_urls": { "Homepage": "https://github.com/VldmrB/youtube-watched" }, "release_url": "https://pypi.org/project/youtubewatched/0.1.4/", "requires_dist": [ "beautifulsoup4 (==4.7.1)", "dash (==0.40.0)", "Flask (==1.0.2)", "google-api-python-client (==1.7.8)", "lxml (==4.3.3)", "numpy (==1.16.2)", "pandas (==0.24.2)", "plotly (==3.7.1)" ], "requires_python": ">=3.6.0", "summary": "Visualization of Youtube watch history from Google Takeout", "version": "0.1.4" }, "last_serial": 5159661, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "b86ca556e39434e53cffbc54f2ebd8ed", "sha256": "f65e7c5678d1f7656856690b00564ae28aef0ef11836e55dcc874f5ef8276982" }, "downloads": -1, "filename": "youtubewatched-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b86ca556e39434e53cffbc54f2ebd8ed", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6.0", "size": 56600, "upload_time": "2019-04-11T15:29:31", "url": "https://files.pythonhosted.org/packages/51/33/26cfb878d757aa0b1cf5f789afa2c2174ccbfee35d7525dd74759c415679/youtubewatched-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "17826fa3062e0a6fffc56bbabbbfda00", "sha256": "514e91b21d76b84b65cdd06797b787a3468949740d768db81b97578d73f60c3d" }, "downloads": -1, "filename": "youtubewatched-0.1.0.tar.gz", "has_sig": false, "md5_digest": "17826fa3062e0a6fffc56bbabbbfda00", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 49736, "upload_time": "2019-04-11T15:29:33", "url": "https://files.pythonhosted.org/packages/e9/60/7c93a2506d25ca3de024a95f4817bae9ac78de246b0a6c2223f3126c0192/youtubewatched-0.1.0.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "47e52fce28bf7df074b056c743ceeb78", "sha256": "bc7f3dcd9e6402d8449014b2fd0bf38799ec3fa50daecd11c61c96c21194f551" }, "downloads": -1, "filename": "youtubewatched-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "47e52fce28bf7df074b056c743ceeb78", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6.0", "size": 57541, "upload_time": "2019-04-13T17:29:06", "url": "https://files.pythonhosted.org/packages/0f/30/e2c1698466b4641ffcbd7c85f595dcdafe199abc3910741ec2202e6f542e/youtubewatched-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b8b476b680db79bcb1e7a73f6d2bdfd6", "sha256": "9a71c474da1f25de45d51822332005d00bb0038a0f2a5f3bb886f47f7d959c70" }, "downloads": -1, "filename": "youtubewatched-0.1.2.tar.gz", "has_sig": false, "md5_digest": "b8b476b680db79bcb1e7a73f6d2bdfd6", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 50604, "upload_time": "2019-04-13T17:29:08", "url": "https://files.pythonhosted.org/packages/09/c7/62cdac3dfb9003236031df540cba8d8989d4bdab04d27221509566ec7b6d/youtubewatched-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "7102cf700d1bac29b853fdd598e1f7c6", "sha256": "2c07b2ce17adf47f4820fde41b061ceb854c5b95f2583e4325d0e76d0a6fb8fa" }, "downloads": -1, "filename": "youtubewatched-0.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "7102cf700d1bac29b853fdd598e1f7c6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6.0", "size": 57682, "upload_time": "2019-04-14T21:26:49", "url": "https://files.pythonhosted.org/packages/e0/85/64767be1975a4d12d370ac8f66d784a8c421e52b029e474f610704260cab/youtubewatched-0.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7658d7fbee4ac6c17ef956e47c2ba30f", "sha256": "68932cf59be331a49ddd41829d5f67fbc04445a3eb5bea0b6d60259e2f47401a" }, "downloads": -1, "filename": "youtubewatched-0.1.3.tar.gz", "has_sig": false, "md5_digest": "7658d7fbee4ac6c17ef956e47c2ba30f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 50718, "upload_time": "2019-04-14T21:26:51", "url": "https://files.pythonhosted.org/packages/a6/ca/3a070292543be5328209b7a8ac0e2a6337b02dbfe09da54a54194fc4e8f8/youtubewatched-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "cf52de6a9a62f4682a8c4e735d9c6381", "sha256": "cc894ebc243805a8c68171c5a76d8bceb29348d66436cb5caef2187f61a26951" }, "downloads": -1, "filename": "youtubewatched-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "cf52de6a9a62f4682a8c4e735d9c6381", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6.0", "size": 57795, "upload_time": "2019-04-18T11:07:22", "url": "https://files.pythonhosted.org/packages/7f/a9/8bfb14bb2a47a5ed29c61bc2f87afadba18eea82a3818f597a3cf8f6e329/youtubewatched-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aab98728c62993768432b0df085ddf05", "sha256": "13a070875bb6e4c272aefc2904ad89224515c557087f6b46e3a7b23edd20a485" }, "downloads": -1, "filename": "youtubewatched-0.1.4.tar.gz", "has_sig": false, "md5_digest": "aab98728c62993768432b0df085ddf05", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 50847, "upload_time": "2019-04-18T11:07:23", "url": "https://files.pythonhosted.org/packages/39/27/c3a34350e3314d9130aeb00f724688a040291637bd02d838bd69292703d7/youtubewatched-0.1.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "cf52de6a9a62f4682a8c4e735d9c6381", "sha256": "cc894ebc243805a8c68171c5a76d8bceb29348d66436cb5caef2187f61a26951" }, "downloads": -1, "filename": "youtubewatched-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "cf52de6a9a62f4682a8c4e735d9c6381", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6.0", "size": 57795, "upload_time": "2019-04-18T11:07:22", "url": "https://files.pythonhosted.org/packages/7f/a9/8bfb14bb2a47a5ed29c61bc2f87afadba18eea82a3818f597a3cf8f6e329/youtubewatched-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aab98728c62993768432b0df085ddf05", "sha256": "13a070875bb6e4c272aefc2904ad89224515c557087f6b46e3a7b23edd20a485" }, "downloads": -1, "filename": "youtubewatched-0.1.4.tar.gz", "has_sig": false, "md5_digest": "aab98728c62993768432b0df085ddf05", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6.0", "size": 50847, "upload_time": "2019-04-18T11:07:23", "url": "https://files.pythonhosted.org/packages/39/27/c3a34350e3314d9130aeb00f724688a040291637bd02d838bd69292703d7/youtubewatched-0.1.4.tar.gz" } ] }