开发者
All the latest news on Android 17, Wear OS 7, and Android XR
Google’s Android 17 update includes highlights like new floating “Bubble” app windows for easier multitasking, a Screen Reaction recording mode, and a 50/50 split gaming mode for foldable phones. Meanwhile, Wear OS 7 brings Live Updates, better battery life for smart watches, and prepares connections for new Android XR smart glasses that will launch this […]
科技前沿
Android 17 starts hitting Pixel phones and watches today
Pixels will get their OTA in the coming weeks, but don't expect monumental changes.
AI 资讯
Android 17 launches with new multitasking tools as Google expands Gemini features
Google has released Android 17 and Wear OS 7, introducing new multitasking features, parental controls, security tools, and smartwatch upgrades. The launch is also accompanied by a Pixel Drop that brings Google’s latest AI models to its devices.
开发者
Google launches Wear OS 7 with Live Updates and a battery life boost
Google's Wear OS 7 update is starting to roll out today for the Pixel Watch 2, 3, and 4 , adding a new Live Updates feature that tracks live events from your Android smart watch. Live Updates will now sync with Wear OS devices, so updates like sports scores or an ongoing meal delivery will […]
AI 资讯
Android 17 arrives on Pixel phones today
Following its official debut last month, Google is now rolling out Android 17 to compatible Pixel phones, alongside additional exclusive features as part of the June Pixel Drop. Not every feature announced alongside the OS at the pre-I/O Android Show is available today though. Android 17 itself is arriving on Pixel phones today, and Google […]
AI 资讯
The Google / Xreal Aura XR glasses are now available to preorder
The Project Aura glasses collaboration between Xreal and Google is now one step closer to being something you can buy. Reservations for the second Android XR device, now dubbed the Xreal Aura, are available for $99 starting today, with a full launch in the US, UK, Japan, Canada, and South Korea expected sometime this Fall. […]
开发者
YouTube字幕突然消失?原来是节点的锅——一次极其小众的排障经历
问题降临:毫无征兆 那天和往常一样,打开YouTube准备看一个英文视频。习惯性地点开字幕按钮—— 没反应。 不是字幕延迟,不是字幕错位,而是整个字幕功能像是从这个世界上蒸发了一样。原始语言的字幕不可用,点进字幕设置一看,连翻译选项都是灰的。没有原始字幕,自然也就没有任何语言的翻译字幕。 一整个功能链,从根部断裂。 第一反应:一定是扩展插件搞的鬼 作为一个浏览器里装了不少扩展和油猴脚本的用户,我的第一直觉非常明确—— 肯定是哪个插件冲突了。 这个判断合情合理。浏览器扩展劫持页面元素、油猴脚本注入自定义代码,这些操作干扰YouTube的正常功能,实在是太常见了。之前遇到过播放器界面异常、按钮消失之类的问题,十次有八次都是扩展惹的祸。 于是我开始了标准排障流程: 禁用所有油猴脚本 → 刷新 → 字幕依然不可用 禁用所有浏览器扩展 → 刷新 → 字幕依然不可用 开无痕模式 (彻底排除扩展和缓存影响)→ 字幕依然不可用 三轮操作下来,扩展插件的嫌疑被彻底洗清。 但这还不是最让人困惑的部分。 真正的诡异之处:薛定谔的字幕 在反复测试的过程中,我发现了一个极其反直觉的现象: 字幕的可用性是随机的。 开着所有扩展 → 有时候字幕 有 ,有时候 没有 关掉所有扩展 → 有时候字幕 有 ,有时候 没有 这完全打破了因果逻辑。如果问题出在扩展上,那么"关掉扩展"就应该稳定地解决问题。但现实是,开和关都呈现随机状态,说明扩展根本不是变量—— 真正的变量藏在别的地方。 这种"薛定谔的字幕"状态让我一度非常迷茫。你没办法用常规的控制变量法去定位一个表现为随机的问题,除非你能找到那个真正在变化的隐藏变量。 灵光一闪:换个节点试试? 在排除了浏览器层面的所有可能之后,我突然想到了一个平时根本不会和"字幕"联系在一起的东西—— 网络节点。 抱着试一试的心态,我切换了代理节点,选了一个不同地区的服务器。 刷新页面。 字幕回来了。 原始字幕、自动翻译、多语言选项——一切恢复正常,仿佛之前的问题从未发生过。 我又切回原来的节点——字幕消失了。再切到新节点——字幕回来了。反复测试了好几次,结果完全一致。 真相大白:问题出在节点上。 恍然大悟:视频和字幕,原来是两套系统 这次经历让我意识到一个之前从未注意到的事实: YouTube的视频流和字幕数据,很可能是由不同的服务器(或CDN节点)分别提供的。 这意味着: 视频能正常播放 ≠ 字幕能正常加载 你的网络可以顺畅地连接到视频服务器,但与此同时,字幕服务器可能对你当前的IP/地区/节点不可达或响应异常 不同的代理节点连接到的Google后端服务器不同,某些节点恰好无法正常获取字幕数据 这也完美解释了之前"随机可用"的现象。我在测试扩展的过程中,代理工具可能在后台自动切换了节点(很多代理工具有负载均衡或自动切换功能),导致有时碰巧连上了能提供字幕的服务器,有时则没有。我一直以为变量是"扩展的开关",实际上真正在暗中变化的是"网络节点"。 技术推测 虽然Google没有公开YouTube的完整架构细节,但根据这次经历可以合理推测: YouTube使用分布式CDN架构 ,视频内容、字幕数据、评论、推荐信息等可能分布在不同的微服务和服务器集群上 字幕API的端点 可能与视频流的端点不同,它们的可用性、地理限制、负载状况都是独立的 某些地区的某些IP段可能因为各种原因(服务器维护、区域限制、DNS解析差异、临时故障)无法正常访问字幕服务 这种问题具有 高度的偶发性和地域性 ,这也是为什么它如此小众,在网上几乎搜不到相关讨论 写在最后 这大概是我遇到过的最小众、最反直觉的技术问题之一。 它小众到什么程度呢?你去搜索"YouTube字幕不可用",得到的答案几乎都是:清除缓存、禁用扩展、检查字幕是否被上传者关闭、换个浏览器试试。 没有人会告诉你"换个代理节点"。 因为在绝大多数人的认知里,"视频都能看"就等于"网络没问题",不会有人把字幕缺失和网络节点联系在一起。 但事实就是这么奇怪: 视频能播放,不代表字幕能加载,因为它们根本就不在同一条路上。 这次经历也给了我一个教训:当排障陷入死胡同的时候,不要只盯着最明显的嫌疑犯。真正的问题,有时候藏在你认为"完全不可能"的地方。 下次再遇到YouTube的某个功能莫名其妙消失,而视频本身却能正常播放的时候——先换个节点试试。说不定,答案就在那里。
AI 资讯
Sundar Pichai faces boos, walkout at Stanford graduation ceremony over Google’s Israel, ICE ties
AI is once again at the heart of a college graduation protest — this time for the technology's use in Google's defense contracts.
AI 资讯
Google Earth's flight simulator mode is now available in your browser
Up, up and away! (Just try not to crash.)
AI 资讯
Google Chrome is closing the loopholes that let old ad blockers keep working
Google Chrome version 150 and 151, expected in late June and July, respectively, will cut off support for the last remaining workarounds for running older ad blockers, 9to5Google reports. Google phased out support for ad-blocking extensions built for Manifest V2, like uBlock Origin, in 2024. At that point, most Chrome users either switched to newer […]
AI 资讯
Google Earth’s flight simulator is now available in your browser
On Friday Google announced it was making a relatively unknown feature of Google Earth available to a wider audience. The desktop version of Google Earth has had a hidden flight simulator mode since 2007, but it's now easily accessible through a web browser without having to download or install any apps. To take to the […]
AI 资讯
I Built a Web App That Finds the Fairest Meeting Spot for Any Group (and It's Free)
The Problem Nobody Talks About Picture this: You're trying to find a place to meet up with friends. Someone suggests a coffee shop. It's 8 minutes from their house. It's 45 minutes from yours. You say yes anyway, because suggesting a different place feels awkward. This happens all the time — with friends, with remote teams, with family scattered across a city. And the worst part? Most "meet in the middle" suggestions aren't actually in the middle. They're just the geographic midpoint, which completely ignores traffic, transit options, and the fact that roads don't go in straight lines. I got frustrated enough to build something about it. Meet Meetle Meetle is a free web app that finds the fairest meeting spot for any group of people — based on real travel times , not just distance. A Chrome Extension is coming soon so you'll have it one click away in your toolbar. You add everyone's starting location, choose how each person is traveling (driving, walking, or transit), hit Find Meeting Point , and Meetle does the math across every person simultaneously. It then surfaces the best nearby cafés, restaurants, parks, gyms, or whatever venue type you're looking for — ranked by actual fairness. No more "it's fine, I don't mind the drive." Now you have data. How It Actually Works Under the hood, Meetle uses three Google Maps APIs working together: Distance Matrix API calculates travel time from every person's location to every candidate venue, simultaneously. This is the core of the fairness scoring — you can't rank venues fairly without knowing everyone's actual travel time to each one. Places API finds candidate venues near the calculated center point. You can filter by type (coffee, food, parks, gyms, etc.), price level, minimum rating, and whether they're open right now. Maps JavaScript API renders everything visually — the map, the travel zones (isochrones), and the markers for each suggested venue. The scoring works two ways and you can toggle between them: Fairness mo
AI 资讯
The future of Hollywood isn’t feeding prompts into vanilla gen AI models
For all the noise that's been made about how generative AI is poised to revolutionize the filmmaking industry, there haven't really been any projects created with the technology that felt like the sort of entertainment people would pay to see. Most AI firms' video models are still only capable of churning out short bursts of […]
AI 资讯
A German Court Has Ruled That Google Is Liable for False Statements Generated by AI Overviews
The ruling holds that a company that designs, trains, operates, and manages an AI system must assume legal liability for any damages caused by the responses it generates.
AI 资讯
I Built a Coding Mascot Generator with Google AI Studio — Meet Octo-Byte! 🐙
This post is my submission for DEV Education Track: Build Apps with Google AI Studio . What I Built I built MascotCraft Studio , an app that generates a cute mascot character for a coding/tutorial brand using Imagen for the visuals and Gemini for the name and personality bio. Here's the prompt I used: "Please create an app that generates a cute mascot character for a coding/tutorial brand, using Imagen for the visuals and Gemini to create a name and short personality description for the mascot. The user should be able to type in a few style keywords (like 'friendly owl', 'cool robot', 'cheerful fox') and get a unique mascot image along with its name and bio." Gemini went well beyond the basic ask — it added a "Character Designer" with quick preset ideas (Wise Python Owl, Cyberpunk JS Fox, Debugging Robo Kitty, and more), color palette options, multiple visual rendering styles (3D Chibi Toy, Minimal Vector, 16-Bit Retro Pixel, Circular Badge), and even a "Studio Gallery Showcase" using localStorage to save and revisit previously generated mascots. Demo 🔗 Live app: https://cute-coding-mascot-generator-924052444918.us-east1.run.app Using the "3D Chibi Toy" style with keywords for a friendly coding octopus, the app generated Octo-Byte — "Asynchronous learning, multi-threaded fun!" A cheerful deep-sea developer who discovered that having eight arms makes multitasking a breeze, whose tech specialty is multi-threaded asynchronous architecture, and whose favorite pastimes include typing on four mechanical keyboards at once. The artwork came out as a glossy 3D chibi-style purple octopus wearing glasses, sitting in front of a tiny code editor. My Experience Watching Gemini's "Thinking" process work through the build was the most interesting part — it planned out the UI sections, color palettes, and visual styles, then added bonus features I never asked for, like the gallery save feature. The whole thing went from a single paragraph prompt to a fully deployed, live web app in
AI 资讯
Switch an old script from Rhino to the V8 runtime
Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to enable the V8 runtime on an existing Apps Script project so I can use modern JavaScript, but I am worried about breaking things that are already working. // appsscript.json — set runtimeVersion to enable V8 // Rollback: change V8 back to DEPRECATED_ES5 { " timeZone " : " America/New_York " , " dependencies " : {}, " exceptionLogging " : " STACKDRIVER " , " runtimeVersion " : " V8 " } // Code.gs — safe V8-compatible replacement for a common Rhino pattern // Rhino allowed: for each (var item in collection) {} // V8 requires standard for...of instead function listSheetNames () { var ss = SpreadsheetApp . getActiveSpreadsheet (); var names = []; var sheets = ss . getSheets (); for ( var i = 0 ; i < sheets . length ; i ++ ) { names . push ( sheets [ i ]. getName ()); } Logger . log ( names . join ( ' , ' )); } The one-line change and why it is a project-wide bomb Open the Apps Script editor, click Project Settings (the gear icon), and check "Show appsscript.json manifest file in editor." Then open that file and change "runtimeVersion": "DEPRECATED_ES5" to "runtimeVersion": "V8" . Save. That is the entire migration from a settings standpoint. What catches people off guard is the failure mode. V8 parses every .gs file in the project as a unit before running anything. One Rhino-only statement — a for each loop, a __iterator__ method, a Date.prototype.getYear call in an otherwise untouched utility file — causes a syntax or runtime error that prevents the whole script from initializing. Not just the file that contains the bad line. Every function, every trigger, the entire project goes dark. The first time I hit this it took me twenty minutes to figure out why a completely unrelated trigger had stopped firing. The error message pointed at the Rhino syntax in a helper file I had not touched in two years. V8 does not isolate the damage; it fails at parse time, before any executi
AI 资讯
Fix "Exceeded maximum execution time" in Apps Script
Originally written for bulldo.gs — republished here with the canonical link pointing home. I'm running a script that processes a large spreadsheet and it keeps dying with "Exceeded maximum execution time" before it finishes. // Checkpoint-resume pattern for long-running sheet jobs function processInBatches () { var props = PropertiesService . getScriptProperties (); var startRow = parseInt ( props . getProperty ( ' lastRow ' ) || ' 2 ' , 10 ); var sheet = SpreadsheetApp . getActiveSpreadsheet (). getActiveSheet (); var lastDataRow = sheet . getLastRow (); var BATCH = 200 ; var SAFE_MS = 5 * 60 * 1000 ; var started = Date . now (); var endRow = Math . min ( startRow + BATCH - 1 , lastDataRow ); var data = sheet . getRange ( startRow , 1 , endRow - startRow + 1 , 5 ). getValues (); for ( var i = 0 ; i < data . length ; i ++ ) { if ( Date . now () - started > SAFE_MS ) { props . setProperty ( ' lastRow ' , String ( startRow + i )); return ; } // process data[i] here } if ( endRow >= lastDataRow ) { props . deleteProperty ( ' lastRow ' ); deleteTrigger_ (); } else { props . setProperty ( ' lastRow ' , String ( endRow + 1 )); } } The 6-minute wall is per-execution, not per-task Apps Script enforces a hard 6-minute execution time limit per run, regardless of whether you're on a free account or a Workspace account (which bumps the limit to 30 minutes, but the same cliff exists). The error doesn't mean your logic is wrong; it means one continuous call to your function took too long. The fix is to stop thinking of your job as a single execution and start thinking of it as a pipeline of short runs. The first time I hit this, I wasted an afternoon trying to speed up the loop. Marginal gains didn't move the needle because the data volume was the real problem — 4,000 rows at one Sheets API call per row will always breach 6 minutes. The correct frame is: how do I save where I stopped and pick up there next run? Saving and restoring a cursor with PropertiesService PropertiesServic
开发者
Merge multiple docs into one in Google Docs
Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to programmatically combine several Google Docs into one file without losing tables or list formatting. // Merges all source docs into destDocId, in order. // Run from the Apps Script editor; no triggers needed. function mergeDocs () { var sourceIds = [ ' DOC_ID_ONE ' , ' DOC_ID_TWO ' , ' DOC_ID_THREE ' ]; var dest = DocumentApp . openById ( ' DEST_DOC_ID ' ). getBody (); for ( var i = 0 ; i < sourceIds . length ; i ++ ) { var srcBody = DocumentApp . openById ( sourceIds [ i ]). getBody (); var total = srcBody . getNumChildren (); for ( var j = 0 ; j < total ; j ++ ) { var el = srcBody . getChild ( j ); var type = el . getType (); if ( type === DocumentApp . ElementType . PARAGRAPH ) { dest . appendParagraph ( el . asParagraph (). copy ()); } else if ( type === DocumentApp . ElementType . TABLE ) { dest . appendTable ( el . asTable (). copy ()); } else if ( type === DocumentApp . ElementType . LIST_ITEM ) { dest . appendListItem ( el . asListItem (). copy ()); } } } } Why there is no single appendElement call The Document service in Apps Script does not expose a generic appendElement method on Body . Every element type has its own typed append method: appendParagraph , appendTable , appendListItem , and so on. That means a merge loop that ignores element types will throw TypeError: el.copy is not a function the moment it hits a table, because you would be passing an Element where the API expects a Table . The fix is to call getType() on each child element and switch on DocumentApp.ElementType . The type enum values are strings like PARAGRAPH , TABLE , LIST_ITEM , INLINE_IMAGE , and HORIZONTAL_RULE . In practice the first three account for almost all real document content. The code above handles those three and silently skips anything else (images, rules) rather than crashing the entire merge. Getting the doc IDs and running the script The ID for any Google Doc is the lo
AI 资讯
Import JSON from an API in Google Sheets
Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to pull live JSON data from an API endpoint directly into a Google Sheet without installing an add-on. // Fetch JSON from an API and write it to the active sheet // Adjust API_URL and the field list to match your endpoint function importJsonFromApi () { var API_URL = ' https://jsonplaceholder.typicode.com/users ' ; var sheet = SpreadsheetApp . getActiveSpreadsheet (). getActiveSheet (); var response = UrlFetchApp . fetch ( API_URL ); var raw = response . getContentText (); var data = JSON . parse ( raw ); var headers = [ ' id ' , ' name ' , ' username ' , ' email ' , ' phone ' ]; var rows = data . map ( function ( obj ) { return headers . map ( function ( key ) { return obj [ key ] || '' ; }); }); sheet . clearContents (); sheet . getRange ( 1 , 1 , 1 , headers . length ). setValues ([ headers ]); sheet . getRange ( 2 , 1 , rows . length , headers . length ). setValues ( rows ); } Why getContentText() comes before JSON.parse UrlFetchApp.fetch() returns an HTTPResponse object, not a string. The first time I skipped getContentText() and passed the response object directly to JSON.parse(), it silently parsed to null and the sheet wrote nothing. You need raw = response.getContentText() to get the actual body as a string, then JSON.parse(raw) turns it into a JavaScript object or array. The URL must be publicly accessible or accept an API key via a query parameter or Authorization header. Add headers like this: UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + token } }). Apps Script's UrlFetchApp quota is 20,000 calls per day on a free Google account, 100,000 on Workspace. The rectangular array constraint — why setValues fails without mapping setValues() is strict: it requires a 2D array where every row has the same number of columns. If you hand it an array of plain JSON objects, it throws 'The number of rows or columns in the range does not match the number of
AI 资讯
Send personalized emails from a sheet in Gmail
Originally written for bulldo.gs — republished here with the canonical link pointing home. I have a spreadsheet of names and email addresses and I want to send each person a personalized message from my Gmail account without copy-pasting or using a paid tool. // Mail merge: Sheet cols A=Name, B=Email, C=Sent // Run from Apps Script; authorize Gmail + Sheets scopes function sendMerge () { var sheet = SpreadsheetApp . getActiveSheet (); var rows = sheet . getDataRange (). getValues (); var quota = MailApp . getRemainingDailyQuota (); var sent = 0 ; for ( var i = 1 ; i < rows . length ; i ++ ) { if ( rows [ i ][ 2 ] === ' Sent ' ) continue ; if ( sent >= quota ) { Logger . log ( ' Quota reached at row ' + ( i + 1 )); break ; } var name = rows [ i ][ 0 ]; var email = rows [ i ][ 1 ]; var subject = ' Hey ' + name + ' , here is your update ' ; var body = ' Hi ' + name + ' , \n\n Your personalized content goes here. \n\n Thanks ' ; MailApp . sendEmail ( email , subject , body ); sheet . getRange ( i + 1 , 3 ). setValue ( ' Sent ' ); sent ++ ; } } Set up your sheet and open the script editor Put names in column A, email addresses in column B, and leave column C blank — the script writes 'Sent' there as it goes. Header row in row 1 is assumed; the loop starts at index 1 (row 2) to skip it. Open the script editor from Extensions > Apps Script, paste the function, and save. The first time you run sendMerge() Google will ask you to authorize two scopes: Sheets (read/write the active spreadsheet) and Gmail (send mail on your behalf). Both are required. If you only see a Sheets prompt, delete the file and re-paste — a cached partial authorization sometimes skips the Gmail scope on older script files. Why the Sent column is the whole point Consumer Google accounts cap at roughly 100 outgoing recipients per 24-hour rolling window via MailApp. If your list has 200 rows and you run the script at 11 pm, it will send 100 and log 'Quota reached at row 101'. Without the Sent check, a sec