Closed ComputerBread closed 1 year ago
Let's talk about caching. To minimize the server's load, I need to cache requests. I first thought about implementing a LRU cache in JavaScript and store the data in the localstorage or indexedDB, but got lazy. I started looking at the Cache Web API, but you still need to do a lot of work:
Items in aĀ CacheĀ do not get updated unless explicitly requested; they don't expire unless deleted. You are also responsible for periodically purging cache entries.)
So instead, I will use the "default HTTP cache" (which you can't really access directly (unless I am wrong)). By default fetch
use the cache, the browser looks for a matching request in its HTTP cache:
The good thing is that I don't really need to do anything (except making sure I am sending the HTTP header, Cache-control: max-age=<value>
). But it also means that I have almost no control, the browser does everything, and the browser can delete the whole cache (even if it does, I guess it's not that bad).
No control mean: no size limit, will it slow down the user's browser?
I don't really want to think about it too much, I think using the default cache should be good enough. If it becomes a problem I will implement my own. Now the question is about how long should the data be cached, I am pretty sure, 99% of users will never update their permissions, so in practice, the data will never expires but still, what's a good value? 1 day, 2 days, 1 week? Should I make a difference between present and absent youtubers?
fresh and stale
Fresh state usually indicates that the response is still valid and can be reused.
Stale state means that the cached response has already expired.
The criterion used is age. In HTTP, age is the time elapsed since the response was generated.
In HTTP/1.1, server can specify the header Cache-control: max-age=<value_in_seconds>
to specify when the response should expire.
When a response is stored in a shared cache, itās necessary to inform the client of the responseās age, to do that, the cache can add the HTTP header Age: nb_secs
indicating how long the response has been cached.
In HTTP/1.0, freshness used to be specified by theĀ Expires
Ā header. (Obsolete, cause uses an explicit time (like Tue, 28 Feb 2022 22:22:22 GMT
) which is harder to parse).
validation
stale responses are not immediately discarded. HTTP has a mechanism to transform a stale response into a fresh one by asking the origin server. This is called validation (or revalidation).
Validation is done by using a conditional request that includes an If-Modified-Since
Ā orĀ If-None-Match
Ā request header.
I didn't know about conditional request before, it could definitely be useful for the full list, but to get a single permissions I think it could be worse. To implement conditional requests, the server needs to return at least one of Last-Modified or ETag HTTP headers.
:warning: note on cache being disabled
There's an option to disable the cache in the DevTools > Network. In firefox it was disable for some reason, and I lost a bit of time because of it ^^
Content scripts don't have access to the chrome.action
API (source), so I need to take care of modifying the icon in the background.
1. content script is injected
2. (optional) send message to background worker to change icon to loading icon
3. fetch permissions
4. send permission to background worker
5. background worker takes care of the logic
So, I need to learn about background worker. I also need to figure out:
intuitively I would say:
When the first page you load isn't a video, the meta tag, found using document.querySelector('meta[itemprop="datePublished"]')
, isn't present on any pages afterward.
If you start with a video it's present on every pages!
This tag is useful because it has a nice "yyyy-mm-dd" format (like "2022-07-31").
We can find the publication date using document.querySelector("#info-strings > yt-formatted-string").innerText
but it returns a localeDateString
(ex: Jul 31, 2022 or 7 avr. 2023 in french)
const event = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
const options = { year: 'numeric', month: 'short', day: 'numeric' };
console.log(event.toLocaleDateString('de-DE', options)); //"20. Dez. 2012"
console.log(event.toLocaleDateString('ar-EG', options)); // "Ł¢Ł ŲÆŁŲ³Ł
ŲØŲ± Ł¢Ł Ł”Ł¢"
console.log(event.toLocaleDateString("fr-FR", options)); // "20 dƩc. 2012"
console.log(event.toLocaleDateString('en', options)); // "Dec 20, 2012"
But these strings are annoying to parse, I will need to use something like date-fns to help me:
import { parse } from "date-fns";
function parseDate(dateString, locale) {
return parse(dateString, 'd LLL y', new Date(), { locale });
}
const frenchDateStr = '7 avr. 2023';
const parsedDate = parseDate(frenchDateStr, require('date-fns/locale/fr'));
console.log(parsedDate); // Output: 2014-04-07T00:00:00.000Z
I found the list of country code used by google using the API:
"af", "am", "ar", "as", "az", "be", "bg", "bn", "bs", "ca", "cs", "da", "de", "el", "en-GB", "en-IN", "en", "es", "es-419", "es-US", "et", "eu", "fa", "fi", "fil", "fr-CA", "fr", "gl", "gu", "hi", "hr", "hu", "hy", "id", "is", "it", "iw", "ja", "ka", "kk", "km", "kn", "ko", "ky", "lo", "lt", "lv", "mk", "ml", "mn", "mr", "ms", "my", "no", "ne", "nl", "or", "pa", "pl", "pt", "pt-PT", "ro", "ru", "si", "sk", "sl", "sq", "sr-Latn", "sr", "sv", "sw", "ta", "te", "th", "tr", "uk", "ur", "uz", "vi", "zh-CN"
For now, I just set a condition to use "0000-00-00" when we can't find the meta tag, because I don't want to waste too much time, I don't want to deal with a build step right now, I want to be done with the overall logic before! But once I am done, here what I can do:
prerequisites:
[ ] find list of all possible locale (https://www.localeplanet.com/icu/ or https://stackoverflow.com/a/3191729 or something else) (put them in excel sheet, or program array)
[ ] compare them to the one supported by youtube
// source: https://developers.google.com/youtube/v3/docs/i18nLanguages/list
"af", "am", "ar", "as", "az", "be", "bg", "bn", "bs", "ca", "cs", "da", "de", "el", "en-GB", "en-IN", "en", "es", "es-419", "es-US", "et", "eu", "fa", "fi", "fil", "fr-CA", "fr", "gl", "gu", "hi", "hr", "hu", "hy", "id", "is", "it", "iw", "ja", "ka", "kk", "km", "kn", "ko", "ky", "lo", "lt", "lv", "mk", "ml", "mn", "mr", "ms", "my", "no", "ne", "nl", "or", "pa", "pl", "pt", "pt-PT", "ro", "ru", "si", "sk", "sl", "sq", "sr-Latn", "sr", "sv", "sw", "ta", "te", "th", "tr", "uk", "ur", "uz", "vi", "zh-CN"
(for the one not supported by youtube ā delete them? associate them to english?)
[ ] get list of locale supported by date-fns https://github.com/date-fns/date-fns/blob/main/src/locale/index.ts
[ ] associate locale from navigator.language with existing date-fns locales.
[ ] what to do when locale is missing?
[ ] set up the thing
bun init
bun add date-fns
algo:
const userLocale = navigator.language;
document.querySelector("#info-strings > yt-formatted-string").innerText
build:
bun build ./index.ts --outdir ./out --minify
For firefox, the manifest needs to be a bit different, for now I just have 2 manifest.json files. I will deal with it later! It seems to work alright in firefox
Ok, I think I am done building the alpha version! probably a lot of bugs, surely it's not entirely done (like shouldn't the extension ask for permission to access a website?) but I really want to close this issue ^^, just press the button
Explanation
Goal for extension is to do something like:
figure out code color & icons for different cases: Nah, I am just going to make my own, it will be composed of 2 parts: left: live reaction, right: upload. Values:
Tool: https://www.pixilart.com/draw?ref=home-page
From https://developer.chrome.com/docs/extensions/mv3/manifest/icons/
Problems
I don't think so no š¢ā We can grab the handle channel URLwithdocument.getElementById("text").querySelector("a").href
("more direct" or faster, but id "text" is kind of weird) or withdocument.getElementById("channel-name").querySelector("a").href
(I think it's better)[ ] I need to store it when yt sets permissionsāproblem: handle URL can be changed, so there's a non-zero risk to have outdated datause video url, fetchGET https://www.googleapis.com/youtube/v3/videos?part=snippet&id=<video_id>
, then getresponse.items[0].snippet.channelId
prosfix problemnever out of datedon't mess with the DOM to find handle URLdon't need to modify existing codecons:require API call (to do in backend, unless ready to share API key (bad idea) OR ask extension user to sign in with Google)āapi call limitation (10,000/day)ā solution 2: store handle URL & have a cron that verify URL didn't change (I don't like this)document.querySelector("yt-button-shape > a").href
bruh, just open your š morondocument.querySelectorAll("yt-button-shape > a")
returns 6 values with the same ChannelID.useful stuff
Todo
check if channelID is in the cacheif not, fetch permissionsand store information in a LRU cache (using localstorage or indexedDB?) (could be an opportunity to learn about indexedDB)youtube is a single-page application, so the content script is injected only once, and doesn't reload when changing to a different video, and because there are some loading delays I need to be a bit careful with timing
detect url changemake sure to wait for the right time to start looking for channelID & publication datemaybe I can listen to a specific network request?maybe I can use a good event?chrome.tab.query({ active: true, currentWindow: true}, ...)
https://developer.chrome.com/docs/extensions/reference/tabs/#get-the-current-tabclosing windowservice worker should delete info about all videos of windowdon't need to, I am using sessionStorageDef needs my attention rn