image-info-from-finder
by John Lindquist
// Author: John Lindquist// Twitter: @johnlindquist// Description: Displays Image Info of Selected Fileimport "@johnlindquist/kit"let sharp = await npm("sharp")let metadata = await sharp(await getSelectedFile()).metadata()await div(md(`~~~json${JSON.stringify(metadata, null, "\t")}~~~`))
// Shortcode: mdn// Menu: Search MDN// Description: Search and open MDN docs// Author: John Lindquist// Twitter: @johnlindquistlet searchIndexResponse = await get(`https://developer.mozilla.org/en-US/search-index.json`)let url = await arg(`Select doc:`,searchIndexResponse.data.map(({ title, url }) => ({name: title,description: url,value: `https://developer.mozilla.org${url}`,})))exec(`open '${url}'`)
// Menu: Word Game// Description: Guess letters to win!// Author: John Lindquist// Twitter: @johnlindquistlet playAgain = truewhile (playAgain) {let {data: [word],} = await get(`https://random-word-api.herokuapp.com/word`)let correct = falselet guesses = []while (!correct) {let [...letters] = await arg({ placeholder: "Guess a letter/s:", hint: word }, //remove hint to make it more challenging 😉word.split("").map(char => (guesses.includes(char) ? char : "*")).join(""))guesses = guesses.concat(...letters)correct = word.split("").every(char => guesses.includes(char))}playAgain = await arg(`🏆 "${word}"! Play Again?`, [{ name: "Yes", value: true },{ name: "No", value: false },])}
// Menu: My IP// Description: Displays and copies IP to clipboard// Author: John Lindquist// Twitter: @johnlindquistlet network = await npm("network")let { promisify } = await npm("es6-promisify")let ip = await promisify(network.get_public_ip)()copy(ip)await arg(ip)
// Menu: Google Home Speak Text// Description: Tell Google Home to speak a message// Author: John Lindquist// Twitter: @johnlindquistlet GoogleHome = await npm("google-home-push")// Find your device IP on your router or// Home App -> Device -> Settings Gear -> Device Informationlet home = new GoogleHome("10.0.0.3")home.speak(await arg("Speak:"))
// Menu: Open Graph Image Grabber// Description: Attempts to scrape the Open Graph image from the focused chrome tab// Author: John Lindquist// Twitter: @johnlindquist//📣 Note: Playwright takes ~20 seconds or so to install...let { getActiveTab } = await kit("chrome")let { attribute } = await kit("playwright")let { copyPathAsPicture } = await kit("file")let download = await npm("image-downloader")let sharp = await npm("sharp")let tab = await getActiveTab()console.log({ tab })setPlaceholder(`og:image of ${tab}`)let url = ""try {url = await attribute(tab,'head meta[property="og:image"]',"content")} catch (error) {setPlaceholder(`og:image not found. Checking twitter:image`)try {url = await attribute(tab,'head meta[name="twitter:image"]',"content")} catch (error) {console.log(error)setPlaceholder(`Sorry, giving up`)await wait(1000)exit()}}console.log({ url })setPlaceholder(`Found ${url}`)console.log({ url })let checkRedirects = await get(url)url = checkRedirects.request.res.responseUrlconsole.log({ redirectedUrl: url })let imageName = tab.split("/").pop()if (!imageName)imageName = tab.split("//").pop().replace("/", "")if (!imageName.endsWith(".png"))imageName = `${imageName}.png`console.log({ imageName })let dest = kenvPath("tmp", imageName)let { filename: image } = await download.image({url,dest,}).catch(error => {console.log(error)})console.log({ image })let width = parseInt(await arg("Enter width:"), 10)setPlaceholder(`Resizing to ${width}`)let metadata = await sharp(image).metadata()let newHeight = Math.floor(metadata.height * (width / metadata.width))let lastDot = /.(?!.*\.)/let resizedImageName = image.replace(lastDot, `-${width}.`)await sharp(image).resize(width, newHeight).toFile(resizedImageName)console.log({ resizedImageName })await copyPathAsPicture(resizedImageName)setPlaceholder(`Copied to clipboard`)await wait(500)
// Menu: Update Twitter Name// Description: Change your name on twitterlet Twitter = await npm("twitter-lite")let envOptions = {hint: md(`You need to [create an app](https://developer.twitter.com/en/apps) to get these keys/tokens`),ignoreBlur: true,secret: true,}let client = new Twitter({consumer_key: await env("TWITTER_CONSUMER_KEY",envOptions),consumer_secret: await env("TWITTER_CONSUMER_SECRET",envOptions),access_token_key: await env("TWITTER_ACCESS_TOKEN_KEY",envOptions),access_token_secret: await env("TWITTER_ACCESS_TOKEN_SECRET",envOptions),})let name = await arg("Enter new twitter name:")let response = await client.post("account/update_profile", {name,}).catch(error => console.log(error))
//Shortcut: opt r/*** @typedef {typeof import("reddit")} Reddit*//*First, create a Reddit App.Click "Create app"For simple scripts, you can select a type of "script".You can enter anything in the "about URL" and "redirect URL" fields.Note your app ID (appears below the app name) and your app secret.*//** @type Reddit */let Reddit = await npm("reddit")let envOptions = {ignoreBlur: true,hint: md(`[Create a reddit app](https://www.reddit.com/prefs/apps)`),secret: true,}let reddit = new Reddit({username: await env("REDDIT_USERNAME"),password: await env("REDDIT_PASSWORD"),appId: await env("REDDIT_APP_ID", envOptions),appSecret: await env("REDDIT_APP_SECRET", envOptions),userAgent: `ScriptKit/1.0.0 (https://scriptkit.com)`,})let subreddits = ["funny","aww","dataisbeautiful","mildlyinteresting","RocketLeague",]subreddits.forEach(sub => {onTab(sub, async () => {await arg("Select post to open:", async () => {let best = await reddit.get(`/r/${sub}/hot`)return best.data.children.map(({ data }) => {let {title,thumbnail,url,subreddit_name_prefixed,preview,} = datalet resolutions = preview?.images?.[0]?.resolutionslet previewImage =resolutions?.[resolutions?.length - 1]?.urlreturn {name: title,description: subreddit_name_prefixed,value: url,img: thumbnail,...(previewImage && {preview: md(`![${title}](${previewImage})### ${title}`),}),}})})})})
await run("speak-text", "I like tacos", "--voice", 5)
// Menu: Speak Text// Description: Speaks Text Using Google's Text-to-Speech// Author: John Lindquist// Twitter: @johnlindquist// Requires a Google Cloud account and configuration:// https://cloud.google.com/text-to-speechlet { playAudioFile } = await kit("audio")let { format } = await npm("date-fns")/** @type typeof import("@google-cloud/text-to-speech") */let textToSpeech = await npm("@google-cloud/text-to-speech")let client = new textToSpeech.TextToSpeechClient()let text = await arg("What should I say?")let voicesDB = db("voices", { voices: [] })let voices = voicesDB.get("voices").value()//cache voicesif (voices.length === 0) {let [{ voices: englishVoices }] = await client.listVoices({languageCode: "en",})let voiceChoices = englishVoices.map(voice => {return {name: `${voice.ssmlGender} - ${voice.name}`,value: {...voice,languageCode: voice.name.slice(0, 4),},}})voicesDB.set("voices", voiceChoices).write()voices = voicesDB.get("voices").value()}// From the terminal or run// speak-text "I like tacos" --voice 5// await run("speak-text", "I like tacos", "--voice", "5")let voice =typeof arg?.voice === "number"? voices[arg?.voice].value: await arg("Select voice", voices)let effectsProfileId = ["headphone-class-device"]let createRequest = (voice, text) => {let speakingRate = 1return {input: { text },voice,audioConfig: {audioEncoding: "MP3",effectsProfileId,speakingRate,},}}let request = createRequest(voice, text)let safeFileName = text.slice(0, 10).replace(/[^a-z0-9]/gi, "-").toLowerCase()let date = format(new Date(), "yyyy-MM-dd-hh-mm-ss")let fileName = `${date}-${safeFileName}.mp3`// Performs the text-to-speech requestlet [response] = await client.synthesizeSpeech(request)// Write the .mp3 locallylet textAudioPath = tmp(fileName)await writeFile(textAudioPath,response.audioContent,"binary")playAudioFile(textAudioPath)
// Menu: Google Image Grid// Description: Create a Grid of Images// Author: John Lindquist// Twitter: @johnlindquistlet gis = await npm("g-i-s")await arg("Search for images:", async input => {if (input.length < 3) return ``let searchResults = await new Promise(res => {gis(input, (_, results) => {res(results)})})return `<div class="flex flex-wrap">${searchResults.map(({ url }) => `<img class="h-32" src="${url}" />`).join("")}</div>`})
// Menu: Change Extension// Description: Drag and drop files to change their extension// Author: John Lindquist// Twitter: @johnlindquist/** @type {File[]} */let droppedFiles = await arg("Drop files")let extension = await arg({placeholder: "Extension",hint: "ts, js, mp4, etc",})for await (let { path } of droppedFiles) {let tsPath = path.replace(/[^.]*$/, extension)mv(path, tsPath)}
//Menu: GitHub Advanced Search//Description: Search GitHub for Code//Author: John Lindquist//Twitter: @johnlindquistlet languages = ["JavaScript","TypeScript","C","C#","C++","CoffeeScript","CSS","Dart","DM","Elixir","Go","Groovy","HTML","Java","Kotlin","Objective-C","Perl","PHP","PowerShell","Python","Ruby","Rust","Scala","Shell","Swift",]let query = await arg("Enter search query:")query = query.replace(" ", "+")let lang = await arg("Select language:", languages)let url = `https://github.com/search?l=&q=${query}+language%3A${lang}&type=code`exec(`open "${url}"`)
// Menu: Rotate Dropped Image// Description: Drop an image in and rotate it// Author: John Lindquist// Twitter: @johnlindquistlet sharp = await npm("sharp")let [file] = await arg({placeholder: "Drop an image",drop: true,ignoreBlur: true,})await arg("Rotate:", async input => {let rotate = 0try {rotate = parseInt(input || 0, 10)} catch {}let buffer = await sharp(file.path).rotate(rotate).toBuffer()return `<img style="height:10rem" src="data:image/jpg;base64,${buffer.toString("base64")}" alt="">`})
//Menu: Testing Arg Panel//Description: Testing the sequence//Author: johnlindquist//Twitter: @johnlindquistlet showGif = await arg("You can display anything",`<div class="flex justify-center"><img src="https://media3.giphy.com/media/efCPDKBWq8qfZuYR34/giphy-downsized-medium.gif?cid=0b9ef2f4xptdlgxr2p76xp8gi9hezyptzhbhgggo76e30zcl&rid=giphy-downsized-medium.gif" alt=""></div>`)let showLinks = await arg("Including links",md(`[Visit scriptkit.app](http://scriptkit.app)[Join the discussion](http://github.com/johnlindquist/kit/discussions)`))let term = await arg("This still returns the input",md(`## What kind of joke would you like to hear?`))let joke = await arg(`Here's a ${term} joke to get your day started:`,async () => {let response = await get(`https://icanhazdadjoke.com/search?term=${term}`,{headers: {Accept: "text/plain",},})return md(`${response.data.split("\n").filter(string => string?.trim()?.length).map(joke => `* ${joke}`).join("")}`)})let autoUpdate = await arg("Auto-update works too!",input => `Have a ${input} day!`)
// Menu: Google// Description: Query google. Open link.// Author: johnlindquist// Twitter: @johnlindquist//https://www.npmjs.com/package/google-itlet googleIt = await npm("google-it")let link = await arg("Search Google", async query => {if (query?.length < 3) return []return (await googleIt({ query })).map(({ title, link, snippet }) => ({name: `${title} + ${link}`,value: link,description: snippet,}))})exec(`open ${link}`)