Hello.

I am Paul Kinlan.

A Developer Advocate for Chrome and the Open Web at Google.

Puppeteer Go

Paul Kinlan

मुझे The Headless Web पसंद है - यह मुझे The Headless Web के विचारों के साथ खेलने की सुविधा देता है - जो कि एक दृश्यमान ब्राउज़र के बिना वेब में चल रहा है और यहां तक कि DOM-curl (कर्ल जो जावास्क्रिप्ट चलाता है) जैसे उपकरण भी बनाता है। विशेष रूप से मुझे ब्राउज़र को स्क्रीप्ट करने, हेरफेर करने और पृष्ठों के साथ सहभागिता करने में स्क्रिप्टिंग पसंद है।

एक डेमो जो मैं बनाना चाहता था, वह Capturing 422 live images के Capturing 422 live images पोस्ट से प्रेरित था जहाँ उसने एक कठपुतली की स्क्रिप्ट चलाई थी जो कई पृष्ठों पर नेविगेट करेगी और एक स्क्रीनशॉट लेगी। कई पृष्ठों पर जाने के बजाय, मैं पृष्ठ पर तत्वों के कई स्क्रीनशॉट लेना चाहता था।

मेरे पास कठपुतली के साथ जो समस्या है, वह एक प्रारंभिक श्लोक है जिसे आपको कुछ भी करने की आवश्यकता है। लॉन्च, ओपन टैब, नेविगेट करें - यह जटिल नहीं है, यह सरल स्क्रिप्ट के लिए बनाने की तुलना में सिर्फ अधिक बॉयलरप्लेट है। इसलिए मैंने Puppeteer Go बनाया। यह सिर्फ एक छोटी सी स्क्रिप्ट है जो सीएलआई उपयोगिताओं को आसानी से बनाने में मदद करती है जो ब्राउज़र को खोलती है, एक पेज पर नेविगेट करती है, your एक्शन करती है और उसके बाद खुद को साफ करती है।

इसकी जांच - पड़ताल करें।

const { go } = require('puppeteer-go');

go('https://paul.kinlan.me', async (page) => {
    const elements = await page.$$("h1");
    let count = 0;
    for(let element of elements) {
      try {
        await element.screenshot({ path: `${count++}.png`});
      } catch (err) {
        console.log(count, err);
      }
    }
});

उपरोक्त कोड मेरे ब्लॉग में h1 तत्व को खोजेगा और स्क्रीनशॉट लेगा। यह कहीं नहीं के रूप में पास के काम के रूप में अच्छा है, लेकिन मैंने सोचा कि यह देखने के लिए साफ था कि क्या हम पेज से सीधे canisuse.com से स्क्रीनशॉट खींच सकते हैं।

const { go } = require('puppeteer-go');

go('https://caniuse.com/#search=css', async (page) => {
    const elements = await page.$$("article.feature-block.feature-block--feature");
    let count = 0;
    for(let element of elements) {
      try {
        await element.screenshot({ path: `${count++}.png`});
      } catch (err) {
        console.log(count, err);
      }
    }
});
4.png
3.png
2.png
1.png
0.png

का आनंद लें!

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium

A simple video insertion tool for EditorJS

Paul Kinlan

मुझे वास्तव में EditorJS पसंद है। यह मुझे अपने स्थैतिक ह्यूगो ब्लॉग के लिए एक बहुत ही सरल वेब-होस्टेड इंटरफ़ेस बनाने देता है।

EditorJS के पास एक साधारण ब्लॉक-आधारित संपादक में मेरी सबसे अधिक जरूरत है। इसमें हेडर, कोड, और यहां तक कि होस्टिंग इंफ्रास्ट्रक्चर की आवश्यकता के बिना संपादक में छवियां जोड़ने का एक सरल तरीका है। यह संपादक के लिए अब तक वीडियो जोड़ने का एक आसान तरीका नहीं है।

मैंने simple-image प्लगिन रिपॉजिटरी ली और इसे बदल दिया (सिर्फ एक tad) बनाने के लिए एक simple-video प्लगइन ( npm module )। अब मैं इस ब्लॉग में आसानी से वीडियो शामिल कर सकता हूं।

यदि आप EditorJS के साथ पारिवारिक हैं, तो अपनी परियोजनाओं में शामिल करना सरल है। बस इसे निम्नानुसार स्थापित करें

npm i simple-video-editorjs

और फिर इसे अपनी परियोजना में शामिल करें जैसा कि आप फिट देखते हैं।

const SimpleVideo = require('simple-video-editorjs');

var editor = EditorJS({
  ...
  
  tools: {
    ...
    video: SimpleVideo,
  }
  
  ...
});

संपादक के पास कुछ सरल विकल्प हैं जो आपको कॉन्फ़िगर करते हैं कि वीडियो को पृष्ठ में कैसे होस्ट किया जाना चाहिए:

  1. ऑटोप्ले - पेज लोड होने पर वीडियो अपने आप प्ले हो जाएगा
  2. म्यूट किया गया - क्या वीडियो में डिफ़ॉल्ट रूप से ध्वनि नहीं होगी (ऑटोप्ले के लिए आवश्यक)
  3. नियंत्रण - क्या वीडियो में डिफ़ॉल्ट HTML नियंत्रण होगा।

नीचे एक वीडियो का एक त्वरित उदाहरण है जो एम्बेडेड है (और कुछ विकल्पों को दिखा रहा है)।

वैसे भी, मुझे इस छोटे से प्लगइन को बनाने में मज़ा आया - यह बनाने में बहुत मुश्किल नहीं था और केवल एक चीज के बारे में जो मैंने किया था उसे बेस 64 में रूपांतरण को स्थगित कर दिया गया था जो कि सरल-चित्र का उपयोग करता है और इसके बजाय बस ब्लॉब यूआरएल का उपयोग करता है।

Test post Video upload

Paul Kinlan

यदि आप यहां एक वीडियो देखते हैं, तो यह काम करता है।

Read More

Friendly Project Name Generator with Zeit

Paul Kinlan

मुझे उन परियोजनाओं के लिए कुछ विचार मिले हैं जो वेब पर साइटें बनाना आसान बनाते हैं - विचारों में से एक है zeit आधारित परियोजनाओं के लिए एक netlify-like drag and drop interface बनाना।

इस पोस्ट में पहेली के सिर्फ एक छोटे से टुकड़े को शामिल किया गया है: प्रोजेक्ट नाम बनाना।

Glitch इसका एक अच्छा उदाहरण है, जब आप एक परियोजना बनाते हैं तो यह इसे एक अनियमित यादृच्छिक रूप से उत्पन्न नाम देता है। टीम ने एक good dictionary of fairly safe words भी बनाया जो अच्छी तरह से संयोजित होता है (और यदि आप चाहते हैं कि उनके पास होस्ट करने के लिए एक सरल सर्वर हो)।

इसलिए, इस रविवार को साइड प्रोजेक्ट था, serverless-functions और ग्लिच के शब्दकोश का उपयोग करके यादृच्छिक परियोजना के नाम उत्पन्न करने के लिए एक सरल माइक्रो-सर्विस बनाना।

And here it is ( code ), यह बहुत छोटा है और बहुत जटिल नहीं है।

const words = require("friendly-words");

function generate(count = 1, separator = "-") {
  const { predicates, objects } = words;
  const pCount = predicates.length;
  const oCount = objects.length;
  const output = [];

  for (let i = 0; i < count; i++) {
    const pair = [predicates[Math.floor(Math.random() * pCount)], objects[Math.floor(Math.random() * oCount)]];
    output.push(pair.join(separator));
  }

  return output;
}

module.exports = { generate }

यदि आप इसे सीधे अपने प्रोजेक्ट में शामिल नहीं करना चाहते हैं, तो आप https: // friendly-project-name के लिए वेब अनुरोध करके यादृच्छिक प्रोजेक्ट नाम ("XY" के रूप में) उत्पन्न करने के लिए HTTP समापन बिंदु का उपयोग कर सकते हैं। kinlan.now.sh/api/names, जो निम्नलिखित की तरह कुछ लौटाएगा।

["momentous-professor"]

आप यह भी नियंत्रित कर सकते हैं कि गणना = x के क्वेरी-स्ट्रिंग पैरामीटर के साथ कितने नाम उत्पन्न किए जा सकते हैं, उदाहरण के लिए https://friendly-project-name.kinlan.now.sh/api/names?count=100

["melon-tangerine","broad-jury","rebel-hardcover","far-friend","notch-hornet","principled-wildcat","level-pilot","steadfast-bovid","holistic-plant","expensive-ulna","sixth-gear","political-wrench","marred-spatula","aware-weaver","awake-pair","nosy-hub","absorbing-petunia","rhetorical-birth","paint-sprint","stripe-reward","fine-guardian","coconut-jumbo","spangle-eye","sudden-euphonium","familiar-fossa","third-seaplane","workable-cough","hot-light","diligent-ceratonykus","literate-cobalt","tranquil-sandalwood","alabaster-pest","sage-detail","mousy-diascia","burly-food","fern-pie","confusion-capybara","harsh-asterisk","simple-triangle","brindle-collard","destiny-poppy","power-globeflower","ruby-crush","absorbed-trollius","meadow-blackberry","fierce-zipper","coal-mailbox","sponge-language","snow-lawyer","adjoining-bramble","deserted-flower","able-tortoise","equatorial-bugle","neat-evergreen","pointy-quart","occipital-tax","balsam-fork","dear-fairy","polished-produce","darkened-gondola","sugar-pantry","broad-slouch","safe-cormorant","foregoing-ostrich","quasar-mailman","glittery-marble","abalone-titanosaurus","descriptive-arch","nickel-ostrich","historical-candy","mire-mistake","painted-eater","pineapple-sassafras","pastoral-thief","holy-waterlily","mewing-humor","bubbly-cave","pepper-situation","nosy-colony","sprout-aries","cyan-bestseller","humorous-plywood","heavy-beauty","spiral-riverbed","gifted-income","lead-kiwi","pointed-catshark","ninth-ocean","purple-toucan","tundra-cut","coal-geography","icy-lunaria","agate-wildcat","respected-garlic","polar-almandine","periodic-narcissus","carbonated-waiter","lavish-breadfruit","confirmed-brand","repeated-period"]

आप विभाजक के क्वेरी-स्ट्रिंग पैरामीटर के साथ विभाजक को नियंत्रित कर सकते हैं। अर्थात, विभाजक = @, उदा। https://friendly-project-name.kinlan.now.sh/api/names?separator=@

["handsomely@asterisk"]

इस परियोजना का एक बहुत उपयोगी पहलू यह है कि यदि शब्दों का संयोजन आक्रामक होने की ओर जाता है, तो यह सुनिश्चित करना आसान है कि यह फिर से न हो जाए।

यह मानते हुए कि प्रोजेक्ट होस्टिंग बहुत महंगी नहीं है, मैं इस सेवा को बनाए रखूंगा, लेकिन यदि आप कभी भी एक समान माइक्रो-सर्विस बनाना चाहते हैं, तो इसे अपने आप को क्लोन करने के लिए स्वतंत्र महसूस करें।

लाइव उदाहरण

कार्रवाई में एपीआई का एक सुपर त्वरित उदाहरण निम्नानुसार है।

const render = (promise, elementId) => {
  promise.then(async(response) => {
    const el = document.getElementById(elementId);
    el.innerText = await response.text();
  })
};


onload = () => {
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names"), "basic");
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names?count=100"), "many");
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names?separator=@"), "separator");
}

एकल प्रतिक्रिया



कई प्रतिक्रियाएँ



कस्टम विभाजक



{{<कच्चे एचटीएमएल>}}

{{</ raw-html>}

Frankie and Bennys: Pay for your meal via the web

Paul Kinlan

जब भी मैं किसी रेस्तरां को कहता हूं कि आप मोबाइल पर भुगतान कर सकते हैं, तो मैं हमेशा इसकी जांच करता हूं, ज्यादातर इसलिए मैं इस तथ्य से रूबरू हो सकता हूं कि आपको ऐप का उपयोग करने की आवश्यकता है। मेरे आश्चर्य की कल्पना करो जब QR कोड एक वेब-आधारित भुगतान प्रवाह की ओर जाता है ….. और यह काम करता है। बहुत बढ़िया काम फ्रेंकी और बेनी!

Read More

Podroll

Paul Kinlan

मुझे पॉडकास्ट बहुत पसंद है और मैं काफी कुछ सुनता हूं, फिर भी मुझे लगता है कि नए पॉडकास्ट की खोज काफी कठिन प्रक्रिया है और मैं दोस्तों पर भरोसा करता हूं कि वे क्या सुनते हैं। तो उस के साथ दिमाग में यहाँ मेरा podroll । मैं Player.fm उपयोग करता Player.fm जो मेरे अच्छे दोस्त माइक महेमॉफ द्वारा बनाया गया है और आपकी सदस्यता सूची को साझा करने और निर्यात करने की क्षमता है।

Read More

Adding "dark mode" to my blog

Paul Kinlan

मैंने जेरेमी कीथ के post about adding dark mode to his blog देखा और यह सरल लग रहा था, इसलिए मैंने इसे एक चक्कर देने का फैसला किया। यहाँ है diff of the work सभी को देखने के लिए। यह आश्चर्यजनक रूप से आसान था (मेरी ओर से मूर्खतापूर्ण त्रुटियों के बाहर)। सीएसएस चर का समर्थन करने के लिए एक छोटा सा रिफ्लेक्टर था और यह सुनिश्चित करना कि अगर कोई ब्राउज़र है जो सीएसएस कस्टम गुणों का समर्थन नहीं करता है, लेकिन यह इसके बारे में है। मैंने वही काम किया जो जेरेमी ने किया था।

Read More

Using Web Mentions in a static site (Hugo)

Paul Kinlan

मेरा ब्लॉग एक पूरी तरह से स्थिर साइट है, जिसे ह्यूगो के साथ बनाया गया है और इसे ज़िट के साथ होस्ट किया गया है। यह मेरे लिए एक महान समाधान है, एक साधारण ब्लॉग में बहुत सरल तैनाती की प्रक्रिया है और यह बहुत तेज़ी से लोड होता है। सांख्यिकीय रूप से उत्पन्न साइटों में कुछ कमियां होती हैं, सबसे बड़ी तब होती है जब आपको अपने पृष्ठ में एकीकृत होने के लिए गतिशील कुछ भी चाहिए होता है (उदाहरण के लिए टिप्पणियाँ)। गतिशील सामग्री को आसानी से होस्ट करने में सक्षम नहीं होने का मतलब यह होगा कि आप तीसरी पार्टी जावास्क्रिप्ट पर भरोसा करते हैं जो तब आपके पेज पर पूर्ण पहुंच प्राप्त करेगा और आपको नहीं पता होगा कि यह क्या कर रहा है - यह आपके उपयोगकर्ताओं को ट्रैक कर सकता है या आपके पृष्ठ को धीमा कर सकता है। भार।

Read More

Creating a pop-out iframe with adoptNode and "magic iframes"

Paul Kinlan

अद्यतन: 8 अक्टूबर - इस डॉक के साथ महत्वपूर्ण मुद्दों। मैंने इस पोस्ट के बारे में Jake Archibald साथ पकड़ लिया क्योंकि मुझे लगा कि मेरे पास कुछ उपन्यास है, बातचीत के दौरान हमने बहुत सी चीजों को उजागर किया जो इस पोस्ट को कुछ अमान्य बना देती हैं, और मैंने इस प्रक्रिया में बहुत कुछ सीखा है जो मुझे नहीं लगता कि अधिकांश डेवलपर्स जानना। कॉलिंग .append() और .

Read More

Meatspace Augmented Reality: From Chester to Nagoya

Paul Kinlan

Some thoughts on AR after finding some during my travels. TL;DR - cheaper content creation and better discovery tools are needed.

Read More

Photos from Carlisle Castle

Paul Kinlan

मैं हाल ही में लड़कों के साथ छुट्टियां मनाने गया था और हमने Carlisle Castle (दुनिया के पलटाव जेक आर्किबाल्ड का जन्मस्थान) को Carlisle Castle । यह उन बेहतर किलों में से एक है जो हम ब्रिटेन में रहे हैं और यदि आप इस क्षेत्र में हैं तो मैं कुछ समय बिताने की सलाह दूंगा। मैंने यह नहीं देखा कि कार्लिस्ले कैसल इंग्लैंड और स्कॉटलैंड के इतिहास में कितनी महत्वपूर्ण भूमिका निभाते थे, और जब हम वहां थे, तो यह पता लगाना बहुत अच्छा था। स्कॉटलैंड से वापस आने के बाद, मेरे पास संभावित प्रभाव पर विचार करने का मौका था, जो कि ब्रेक्सिट स्कॉटलैंड के साथ हमारे भविष्य के संबंधों पर हो सकता है और यह देखते हुए कि कार्लिस्ले कितना करीबी है, जो जानता है कि स्वतंत्रता के लिए नए सिरे से धक्का होगा तो क्या होगा।

Read More

Idle observation: Indexing text in images

Paul Kinlan

मैं दूसरे दिन (यह एक सुंदर शहर) ललंगोलेन में लड़कों के साथ था और मैं जानकारी के संकेतों की तस्वीर ले रहा था, जिसमें क्षेत्र के कुछ इतिहास शामिल थे ताकि मैं इसे बाद में पढ़ सकूं, और मैंने सोचा कि मैं इस पर गौर करूंगा यदि जानकारी सिर्फ लोगों संकेत अतीत चलने की तुलना में अधिक के लिए उपलब्ध था वेब को देखने के लिए - और यह नहीं है। मैं तो मिला पत्रक के बारे में सोच, उन में सामग्री वेब पर खोजने के लिए लगभग असंभव है।

Read More

Liverpool World Museum

Paul Kinlan

मैं बच्चों को दूसरे सप्ताह लिवरपूल वर्ल्ड हिस्ट्री म्यूज़ियम ले गया, यह बहुत साफ-सुथरा था। लगभग 30 वर्षों में अंतरिक्ष और समय खंड नहीं बदला है, बग परिक्षेत्र का एक बड़ा खंड बंद हो गया था, और एक्वैरियम मुझे याद करने की तुलना में छोटा लग रहा था। मिस्र का खंड खुला था (यह तब नहीं था जब मैं आखिरी बार गया था) और यह बहुत बढ़िया था।

Read More

Bookstore - Llangollen

Paul Kinlan

मुझे यह जगह बहुत पसंद है, यह लालांगोलन के एक कैफे के ऊपर है। मैं लगभग 30 साल पहले अपने दादा-दादी के साथ यहां आया था और अब यह बहुत सुंदर है। मेरी एकमात्र इच्छा यह है कि और भी कॉमिक्स किताबें थीं - मैं कसम खाता हूँ कि जब मैं बच्चा था तो ढेर अधिक था। Check it out

Read More

Webmention.app

Paul Kinlan

मुझे Webmentions के विचार से प्यार है, फिर भी मुझे इसे अपनी साइट पर लागू करने का समय नहीं मिला है। एक उच्च-स्तर के वेब में आपको वेब पर अन्य सामग्री के बारे में टिप्पणी करने, पसंद करने और उसका उत्तर देने की अनुमति है और क्या यह उस सामग्री को दिखाई देता है, जिसे Disqus (जो मैं अपनी साइट से हटाने के लिए उत्सुक हूं) के साथ केंद्रीकृत किए बिना किया जा सकता है।

वेब मेंशन को दो घटकों, प्रेषक और रिसीवर में विभाजित किया गया है। रिसीवर वह साइट है जिसके बारे में मैं एक पोस्ट लिख रहा हूं और उनकी साइट पर कुछ हो सकता है जो उनके ब्लॉग के इनबाउंड लिंक या प्रतिक्रियाओं को दर्शाता है; और प्रेषक है, ठीक है, मुझे। मुझे दूरस्थ साइट को लिखने की ज़रूरत है जो मैंने लिखी है या कुछ सामग्री के लिए प्रतिक्रिया की है जो उन्होंने बनाई है।

समस्या के एक हिस्से को हल करने के लिए बल्कि भयानक Remy Sharp ने webmention.app को बनाया: पिंग भेजना। रेमी के उपकरण से संभावित पावनियों को 'पिंग' भेजना आसान हो जाता है, जिन्हें मैंने सीएलआई लिपि कहकर जोड़ा है।

मैंने अपने ब्लॉग को ह्यूगो और स्टैटिक-बिल्डर टूल का उपयोग करके ज़िट का उपयोग करके होस्ट किया है, इसलिए यह relatively trivial for me to add in support for webmention app था। मैं अभी काम करता npm i webmention और फिर अपनी build.sh फाइल से टूल के सीएलआई संस्करण को कॉल करता हूं - यह वास्तव में इतना आसान है।

अब जब मैं एक पोस्ट बनाता हूं, तो उसे सभी नए URL पर एक त्वरित पिंग भेजना चाहिए कि मैंने उनकी साइट के बारे में कुछ सामग्री बनाई है।

Creating a commit with multiple files to Github with JS on the web

Paul Kinlan

मेरी साइट entirely static । यह Hugo साथ बनाया गया है और Zeit साथ होस्ट किया Zeit । मैं सेटअप के साथ बहुत खुश हूं, मुझे तत्काल बिल्ड और सुपर फास्ट सीडीएनआईडी कंटेंट डिलीवरी के पास मिलता है और मैं उन सभी चीजों को कर सकता हूं जिनकी मुझे आवश्यकता है क्योंकि मुझे किसी भी राज्य का प्रबंधन करने की आवश्यकता नहीं है। मैंने इस साइट के लिए एक simple UI बनाया है और मेरा podcast creator भी है जो मुझे अपने podcast creator रूप से होस्ट की गई साइट पर जल्दी से नई सामग्री पोस्ट करने में सक्षम बनाता है।

Read More

Screen Recorder: recording microphone and the desktop audio at the same time

Paul Kinlan

मेरे पास दुनिया के सबसे सरल स्क्रीन रिकॉर्डिंग सॉफ़्टवेयर के निर्माण का लक्ष्य है और मैं पिछले कुछ महीनों से परियोजना पर धीरे-धीरे नूडल बना रहा हूं (मेरा मतलब वास्तव में धीरे-धीरे है)।

पिछले पोस्टों में मुझे सभी इनपुट स्रोतों से प्राप्त धाराओं के बारे में screen recording and a voice overlay मिला था। हालांकि निराशा का एक क्षेत्र यह था कि मैं यह काम नहीं कर सकता था कि डेस्कटॉप से ऑडियो कैसे प्राप्त किया जाए * और * स्पीकर से ऑडियो को ओवरले करें। मैंने आखिरकार यह करने के लिए कैसे काम किया।

सबसे पहले, Chrome में getDisplayMedia अब ऑडियो कैप्चर करने की अनुमति देता है, विशेष रूप से एक अजीब getDisplayMedia जैसा लगता है कि यह आपको फ़ंक्शन कॉल में audio: true निर्दिष्ट करने की अनुमति नहीं देता है, अब आप कर सकते हैं।

const audio = audioToggle.checked || false;
desktopStream = await navigator.mediaDevices.getDisplayMedia({ video:true, audio: audio });

दूसरी बात, मैंने मूल रूप से सोचा था कि ऑडियो स्ट्रीम में दो ट्रैक बनाने से मैं वह प्राप्त कर सकूंगा जो मैं चाहता था, लेकिन मुझे पता चला कि Chrome का MediaRecorder API केवल एक ट्रैक का उत्पादन कर सकता है, और दूसरा, यह वैसे भी काम नहीं करेगा क्योंकि पटरियों डीवीडी उत्परिवर्ती ऑडियो पटरियों की तरह हैं जो केवल एक समय में खेल सकते हैं।

समाधान शायद बहुत से लोगों के लिए सरल है, लेकिन यह मेरे लिए नया था: वेब ऑडियो का उपयोग करें।

यह पता चला है कि WebAudio API में createMediaStreamSource और createMediaStreamDestination , जो दोनों ही समस्या को हल करने के लिए API की आवश्यकता है। createMediaStreamSource मेरे डेस्कटॉप ऑडियो और माइक्रोफ़ोन से धाराएँ ले सकता है, और दोनों को मिलकर createMediaStreamDestination द्वारा बनाई गई वस्तु में शामिल कर सकता है, यह मुझे इस एक स्ट्रीम को MediaRecorder API में पाइप करने की सुविधा देता है।

const mergeAudioStreams = (desktopStream, voiceStream) => {
  const context = new AudioContext();
    
  // Create a couple of sources
  const source1 = context.createMediaStreamSource(desktopStream);
  const source2 = context.createMediaStreamSource(voiceStream);
  const destination = context.createMediaStreamDestination();
  
  const desktopGain = context.createGain();
  const voiceGain = context.createGain();
    
  desktopGain.gain.value = 0.7;
  voiceGain.gain.value = 0.7;
   
  source1.connect(desktopGain).connect(destination);
  // Connect source2
  source2.connect(voiceGain).connect(destination);
    
  return destination.stream.getAudioTracks();
};

साधारण।

पूरा कोड my glitch पर पाया जा सकता है, और डेमो यहाँ पाया जा सकता है: https://screen-record-voice.glitch.me/

{{<तेज़-youtube oGIdqcMFKlA>}}

Extracting text from an image: Experiments with Shape Detection

Paul Kinlan

मेरे पास Google IO के बाद का समय थोड़ा कम था और मैं अपने द्वारा की गई लंबी अवधि की खुजली को दूर करना चाहता था। मैं बस उस पाठ की प्रतिलिपि बनाना चाहता हूं जो ब्राउज़र में छवियों के अंदर होता है। बस इतना ही। मुझे लगता है कि यह सभी के लिए एक साफ सुथरा फीचर होगा।

सीधे क्रोम में कार्यक्षमता जोड़ना आसान नहीं है, लेकिन मुझे पता है कि मैं एंड्रॉइड पर इरादे प्रणाली का लाभ उठा सकता हूं और अब मैं वेब के साथ (या कम से कम क्रोम एंड्रॉइड पर) कर सकता हूं।

वेब प्लेटफ़ॉर्म में दो नए अतिरिक्त - शेयर लक्ष्य स्तर 2 (या मैं इसे साझा फ़ाइल कॉल करना चाहते रूप में) और TextDetector आकार का पता लगाने एपीआई में - have allowed me to build a utility that I can Share images to and get the text held inside them

मूल कार्यान्वयन अपेक्षाकृत सीधे आगे की ओर है, आप सेवा कार्यकर्ता में एक शेयर लक्ष्य और एक हैंडलर बनाते हैं, और फिर एक बार जब आपके पास यह छवि होती है कि उपयोगकर्ता ने आपको साझा किया है तो आप TextDetector पर TextDetector चलाते हैं।

Share Target API आपके वेब एप्लिकेशन को मूल साझाकरण उप-प्रणाली का हिस्सा होने की अनुमति देता है, और इस मामले में अब आप इसे अपने Web App Manifest अंदर घोषित करके सभी image/* प्रकारों को संभालने के लिए पंजीकरण कर सकते हैं।

"share_target": {
  "action": "/index.html",
  "method": "POST",
  "enctype": "multipart/form-data",
  "params": {
    "files": [
      {
        "name": "file",
        "accept": ["image/*"]
      }
    ]
  }
}

जब आपका PWA स्थापित हो जाता है, तो आप इसे उन सभी स्थानों पर देखेंगे जहाँ आप निम्न प्रकार से चित्र साझा करते हैं:

Share Target API एक फॉर्म पोस्ट की तरह फाइलों को साझा करने का व्यवहार करता है। जब फ़ाइल को वेब ऐप में साझा किया जाता है, तो सेवा कर्मी को सक्रिय किया जाता है फ़ाइल डेटा के साथ fetch हैंडलर लागू किया जाता है। डेटा अब सेवा कार्यकर्ता के अंदर है, लेकिन मुझे इसकी वर्तमान विंडो में आवश्यकता है ताकि मैं इसे संसाधित कर सकूं, सेवा को पता है कि किस विंडो ने अनुरोध किया है, ताकि आप आसानी से ग्राहक को लक्षित कर सकें और उसे डेटा भेज सकें।

self.addEventListener('fetch', event => {
  if (event.request.method === 'POST') {
    event.respondWith(Response.redirect('/index.html'));
    event.waitUntil(async function () {
      const data = await event.request.formData();
      const client = await self.clients.get(event.resultingClientId || event.clientId);
      const file = data.get('file');
      client.postMessage({ file, action: 'load-image' });
    }());
    
    return;
  }
  ...
  ...
}

एक बार जब छवि उपयोगकर्ता इंटरफ़ेस में होती है, तो मैं इसे टेक्स्ट डिटेक्शन एपीआई के साथ संसाधित करता हूं।

navigator.serviceWorker.onmessage = (event) => {  
  const file = event.data.file;
  const imgEl = document.getElementById('img');
  const outputEl = document.getElementById('output');
  const objUrl = URL.createObjectURL(file);
  imgEl.src = objUrl;
  imgEl.onload = () => {
    const texts = await textDetector.detect(imgEl);
    texts.forEach(text => {
      const textEl = document.createElement('p');
      textEl.textContent = text.rawValue;
      outputEl.appendChild(textEl);
    });
  };
  ...
};

सबसे बड़ा मुद्दा यह है कि ब्राउज़र स्वाभाविक रूप से छवि को घुमाता नहीं है (जैसा कि आप नीचे देख सकते हैं), और आकृति डिटेक्शन एपीआई को सही रीडिंग ओरिएंटेशन में पाठ की आवश्यकता है।

मैंने रोटेशन का पता लगाने के लिए EXIF-Js library का उपयोग करने के बजाय आसान का उपयोग किया और फिर छवि को फिर से उन्मुख करने के लिए कुछ बुनियादी कैनवास हेरफेर किया।

EXIF.getData(imgEl, async function() {
  // http://sylvana.net/jpegcrop/exif_orientation.html
  const orientation = EXIF.getTag(this, 'Orientation');
  const [width, height] = (orientation > 4) 
                  ? [ imgEl.naturalWidth, imgEl.naturalHeight ]
                  : [ imgEl.naturalHeight, imgEl.naturalWidth ];

  canvas.width = width;
  canvas.height = height;
  const context = canvas.getContext('2d');
  // We have to get the correct orientation for the image
  // See also https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images
  switch(orientation) {
    case 2: context.transform(-1, 0, 0, 1, width, 0); break;
    case 3: context.transform(-1, 0, 0, -1, width, height); break;
    case 4: context.transform(1, 0, 0, -1, 0, height); break;
    case 5: context.transform(0, 1, 1, 0, 0, 0); break;
    case 6: context.transform(0, 1, -1, 0, height, 0); break;
    case 7: context.transform(0, -1, -1, 0, height, width); break;
    case 8: context.transform(0, -1, 1, 0, 0, width); break;
  }
  context.drawImage(imgEl, 0, 0);
}

और वोइला, यदि आप एक छवि को ऐप में साझा करते हैं तो यह छवि को घुमाएगी और फिर इसे उस पाठ के आउटपुट को वापस करने का विश्लेषण करेगी जो इसे मिला है।

इस छोटे से प्रयोग को बनाने में अविश्वसनीय रूप से मज़ा आया, और यह मेरे लिए तुरंत उपयोगी हो गया है। हालाँकि, यह inconsistency of the web platform उजागर करता है। ये API सभी ब्राउज़रों में उपलब्ध नहीं हैं, वे क्रोम के सभी संस्करण में भी उपलब्ध नहीं हैं - इसका मतलब है कि जैसा कि मैंने इस लेख को क्रोम ओएस लिखा है, मैं ऐप का उपयोग नहीं कर सकता, लेकिन उसी समय, जब मैं इसका उपयोग कर सकता हूं … OMG, बहुत अच्छा।

Wood Carving found in Engakuji Shrine near Kamakura

Sakura

Paul Kinlan

मुझे बताया गया है कि अधिक विशेष रूप से यह 'याज़ाकुरा' है

Read More