Publish YouTube Line 1.0.3.4 with installation and privacy documentation

This commit is contained in:
DepressedCat committed 2026-10-11 00:21:47 +03:00
commit 383b8de85c
32 files changed
+780

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
dist/
node_modules/
.agents/
.runtime/
*.pem
*.key
*.crx
*.log
.env
.env.*
+74
View File
@@ -0,0 +1,74 @@
# Chrome Web Store Submission Notes
Package: `youtube-line-1.0.3.4-chrome-store.zip`
## Store Listing
Name: YouTube Line
Short description:
Locks the YouTube player timeline so progress stays visible while controls fade.
Detailed description:
YouTube Line adds a thin playback progress line to the bottom of the YouTube video player. It keeps video progress visible when the native YouTube controls auto-hide and lets users customize the line colors and height from the extension options page.
Features:
- Persistent YouTube playback progress line.
- Custom primary color, progress color, and line height.
- Runs only on `youtube.com` and `www.youtube.com`.
- No account, analytics, tracking, or external service.
This extension is not affiliated with YouTube or Google.
Suggested category: Accessibility or Productivity.
## Privacy
Single purpose:
Display and customize a persistent playback progress line on YouTube videos.
Remote code:
No. All JavaScript and CSS used by the extension are packaged inside the extension.
Data usage:
The extension does not collect, transmit, sell, or share user data. It stores only the user's color and line-height preferences locally with `chrome.storage.local`.
## Permission Justification
`scripting`
Used to inject the packaged content script and CSS into already-open YouTube tabs after the extension is installed or Chrome starts.
`storage`
Used to save the user's selected line colors and height locally.
Host permissions: `*://youtube.com/*`, `*://www.youtube.com/*`
Required to run the progress-line content script on YouTube pages only.
## Reviewer Test Instructions
1. Install the extension.
2. Open any YouTube video page.
3. Wait for the YouTube controls to fade; the progress line remains visible at the bottom of the player.
4. Open the extension options page, change colors or line height, and save.
5. Reload or revisit the YouTube video page to confirm the customized line is applied.
## Store Assets
- 128x128 store icon: use `icons/128.png`.
- Localized screenshots:
- `store-assets/youtube-line-screenshot-main-1280x800.png`
- `store-assets/youtube-line-screenshot-options-1280x800.png`
- `store-assets/youtube-line-screenshot-before-after-1280x800.png`
- Global screenshots: use the same three `store-assets/youtube-line-screenshot-*.png` files.
- 440x280 small promo tile: `store-assets/youtube-line-promo-small-440x280.png`.
- 1400x560 marquee promo tile: `store-assets/youtube-line-promo-marquee-1400x560.png`.
- Privacy policy URL in the developer account or item privacy section.
+25
View File
@@ -0,0 +1,25 @@
# Данные и конфиденциальность YouTube Line
Документ описывает предоставленный пакет версии 1.0.3.4 и его исходный код.
## Данные в браузере
Расширение сохраняет в `chrome.storage.local` три настройки: `color1` и `color2` — цвета, `height` — высоту линии. Настройки не синхронизируются через `chrome.storage.sync`.
На странице YouTube скрипт получает `currentTime` и `duration` элемента видео для отображения процента воспроизведения. Эти значения используются в памяти страницы и не сохраняются как история просмотров. Служебный маркер в DOM предотвращает повторное создание линии на той же странице.
Фоновый скрипт находит открытые вкладки `youtube.com` и `www.youtube.com`, чтобы подключить к ним локальные скрипты и CSS после установки расширения и запуска браузера. Удалённый исполняемый код не загружается. Скрипт также подключается к страницам YouTube обычным механизмом content scripts из манифеста.
## Передача данных
В коде нет собственного API, аналитики, рекламы, диагностического сервера и передачи настроек или сведений о просмотре разработчику. Расширение не добавляет внешних сетевых запросов для работы линии; все библиотеки и стили входят в пакет.
Обычная работа YouTube и браузера регулируется их собственными правилами. Установка и обновление расширения через Chrome Web Store обслуживаются Google. При посещении репозитория и скачивании ZIP запросы получает сервер Gitea; обычные серверные журналы могут содержать IP-адрес, время, URL запроса, HTTP-статус и User-Agent. Эти журналы не содержат настройки линии или историю просмотра, отправленные расширением.
## Управление настройками
Цвета и высоту можно изменить в параметрах расширения. При удалении расширения браузер удаляет его собственное хранилище; скачанные пользователем архивы остаются в выбранной папке.
Разрешения `storage` и `scripting` используются для хранения настроек и подключения локальных файлов. Доступ ограничен `youtube.com` и `www.youtube.com` в соответствии с `manifest.json`.
Вопросы и сообщения о проблемах можно оставить в разделе Issues репозитория.
+63
View File
@@ -0,0 +1,63 @@
# YouTube Line
![YouTube Line](store-assets/youtube-line-promo-marquee-1400x560.png)
Тонкая линия прогресса внизу видеоплеера YouTube, которая остаётся видимой, когда стандартные элементы управления скрываются. Цвета и высота настраиваются пользователем.
**Версия 1.0.3.4 · Manifest V3 · работает на YouTube**
[Установить из Chrome Web Store](https://chromewebstore.google.com/detail/youtube-line/bffibbndapafegbmeichgohhjcgnpeae?hl=ru) · [Скачать ZIP из релиза](https://git.lonarpgproxy.space/OwnerLink/YouTube-Line/releases/tag/v1.0.3.4) · [Данные и разрешения](PRIVACY.md)
## Возможности
- Показывает прогресс воспроизведения при скрытых элементах управления YouTube.
- Позволяет выбрать цвет фона линии и цвет заполнения.
- Высота линии регулируется от 1 до 20 пикселей; по умолчанию — 2 пикселя.
- Настройки сохраняются локально и применяются к открытым видео через события хранилища браузера.
- Работает на `youtube.com` и `www.youtube.com`.
- Для прямых эфиров дополнительная линия скрывается.
- Отдельный помощник, сервер и установка зависимостей не требуются: JavaScript, CSS и jQuery входят в пакет.
## Установка
Установите расширение из [Chrome Web Store](https://chromewebstore.google.com/detail/youtube-line/bffibbndapafegbmeichgohhjcgnpeae?hl=ru). Обновления устанавливаются браузером.
Для ручной установки:
1. Скачайте `youtube-line-1.0.3.4-chrome-store.zip` из [релиза 1.0.3.4](https://git.lonarpgproxy.space/OwnerLink/YouTube-Line/releases/tag/v1.0.3.4).
2. Распакуйте архив в постоянную папку.
3. Откройте `chrome://extensions` и включите режим разработчика.
4. Нажмите «Загрузить распакованное расширение» и выберите папку с `manifest.json`.
5. Откройте видео на YouTube и дождитесь скрытия стандартных элементов управления.
При ручной установке обновления нужно распаковывать самостоятельно, затем обновлять расширение на странице `chrome://extensions`.
## Настройка
На странице `chrome://extensions` откройте сведения о YouTube Line и перейдите в параметры расширения. Выберите цвета и высоту линии, затем нажмите **Save**.
В текущей версии страница настроек на английском: **Color #1** — фон линии, **Color #2** — заполненная часть, **Line height** — высота в пикселях. Описание расширения локализовано через файлы `_locales`.
![Настройки YouTube Line](store-assets/youtube-line-screenshot-options-1280x800.png)
## Что хранится и какие разрешения нужны
В `chrome.storage.local` сохраняются только выбранные цвета и высота линии (`color1`, `color2`, `height`). Для рисования прогресса расширение читает текущее время и длительность видео в открытой странице. Журнала просмотров, аналитики и отправки данных разработчику в коде нет.
| Разрешение | Назначение |
| --- | --- |
| `storage` | Сохранение цветов и высоты линии в этом браузере. |
| `scripting` | Подключение локальных скриптов и стилей к уже открытым вкладкам YouTube после установки и запуска браузера. |
| Доступ к `youtube.com` и `www.youtube.com` | Отображение линии в плеере YouTube. |
Подробности — [PRIVACY.md](PRIVACY.md). Для установки и обновлений из магазина действуют правила Chrome Web Store; исходники и ZIP обслуживает Gitea.
## Исходники и релиз
Исходники расширения находятся в корне репозитория. ZIP в релизе опубликован без изменений из предоставленного пакета 1.0.3.4. Его содержимое проверено на совпадение с исходниками, контрольная сумма доступна в `SHA256SUMS.txt`.
Сборщик не требуется. Все файлы, указанные в `manifest.json`, включены в пакет. jQuery 3.2.1 поставляется локально; сведения и лицензия библиотеки — [THIRD_PARTY.md](THIRD_PARTY.md).
Если линия не появляется, обновите вкладку YouTube и проверьте, что расширение включено. Она предназначена для обычного плеера `#movie_player`; текущий пакет не заявляет поддержку отдельного плеера Shorts. YouTube может менять разметку плеера; о проблеме можно сообщить в разделе Issues этого репозитория.
YouTube Line — независимое расширение, не связанное с YouTube или Google.
+10
View File
@@ -0,0 +1,10 @@
# Сторонние компоненты YouTube Line
В пакет включён **jQuery 3.2.1**: `files/script/jquery-3.2.1.min.js`.
- Правообладатели: JS Foundation и другие участники проекта.
- Лицензия библиотеки: MIT.
- Оригинальный текст: [jquery/jquery, тег 3.2.1](https://raw.githubusercontent.com/jquery/jquery/3.2.1/LICENSE.txt).
- Локальная копия: [licenses/jquery-3.2.1-LICENSE.txt](licenses/jquery-3.2.1-LICENSE.txt).
Исходный заголовок библиотеки в JavaScript сохранён. Этот документ описывает лицензию jQuery и не назначает лицензию всему расширению.
+1
View File
@@ -0,0 +1 @@
{"description":{"message":"Фиксирует линию статуса проигрывания Ютуб плеера"}}
+5
View File
@@ -0,0 +1,5 @@
{
"description": {
"message": "Locks the YouTube player timeline"
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"description": {
"message": "Фиксирует линию статуса проигрывания Ютуб плеера"
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"description": {
"message": "Фиксирует линию статуса проигрывания Ютуб плеера"
}
}
+68
View File
@@ -0,0 +1,68 @@
body {
width: 100%;
margin: 0;
background: #eee;
overflow: hidden;
}
#body {
display: table;
width: 100%;
height: 100vh;
}
#cell {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#block {
/* display: inline-block;*/
margin: 0 0 20px;
padding: 50px 0;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
#cell div>* {
vertical-align: middle;
}
h1 {
margin: 0 0 20px;
font-size: 16px;
color: #d11c1c;
}
span {
display: inline-block;
padding: 0 20px 0 0;
min-width: 70px;
text-align: right;
}
b {
display: inline-block;
padding: 0 0 0 3px;
font-weight: 400;
color: #bbb;
}
input {
width: 80px;
margin: 5px 5px 5px 0;
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 3px;
outline: none;
text-align: center;
}
button {
margin: 15px 0 0;
padding: 5px 10px;
background: #ccc;
border: none;
border-radius: 3px;
outline: none;
cursor: pointer;
user-select: none;
}
button:hover {
background: #bbb;
}
+20
View File
@@ -0,0 +1,20 @@
#la-player-line {
position: absolute;
width: 100%;
height: 2px;
background: rgba(128, 128, 128, .5);
bottom: 0;
z-index: 99;
opacity: 1;
transition: opacity .25s ease-in-out;
}
#la-player-line div {
position: absolute;
width: 59%;
height: 100%;
background: red;
transition: width .1s ease-in-out;
}
#movie_player:not(.ytp-autohide) #la-player-line {
opacity: 0;
}
+39
View File
@@ -0,0 +1,39 @@
function ignoreInjectionError() {
if (chrome.runtime.lastError) {
return;
}
}
function includeFiles(tabID) {
chrome.scripting.executeScript({
target: { tabId: tabID },
files: [ 'files/script/jquery-3.2.1.min.js' ]
}, function() {
if (chrome.runtime.lastError) {
return;
}
chrome.scripting.executeScript({
target: { tabId: tabID },
files: [ 'files/script/postload.js' ]
}, ignoreInjectionError);
chrome.scripting.insertCSS({
target: { tabId: tabID },
files: [ 'files/css/style.css' ]
}, ignoreInjectionError);
});
}
function includeFilesInOpenYouTubeTabs() {
chrome.tabs.query({
url: [ '*://youtube.com/*', '*://www.youtube.com/*' ]
}, function(tabs) {
for (var i = 0; i < tabs.length; i++) {
includeFiles(tabs[i].id);
}
});
}
chrome.runtime.onInstalled.addListener(includeFilesInOpenYouTubeTabs);
chrome.runtime.onStartup.addListener(includeFilesInOpenYouTubeTabs);
+4
View File
@@ -0,0 +1,4 @@
/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={IDLine truncated
a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e=Line truncated
null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?bLine truncated
+22
View File
@@ -0,0 +1,22 @@
$(document).ready(function(){
chrome.storage.local.get(null, function(obj){
// $('#dragger').css('left', ((obj.opacity - .05) / .9 * 225 + 20) + 'px');
// $('#shape').css('backgroundColor', 'rgba(0, 0, 0, ' + obj.opacity + ')');
if (obj.color1) $('[name="color1"]').val(obj.color1);
if (obj.color2) $('[name="color2"]').val(obj.color2);
if (obj.height) $('[name="height"]').val(obj.height);
});
$('button').click(function(){
var height = $('[name="height"]').val() * 1;
if (!height) height = 2;
height = Math.max(1, Math.min(20, Math.round(height)));
chrome.storage.local.set({
color1: $('[name="color1"]').val(),
color2: $('[name="color2"]').val(),
height: height
});
chrome.tabs.getCurrent(function(tab){
chrome.tabs.remove(tab.id);
});
});
});
+54
View File
@@ -0,0 +1,54 @@
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null;
}
function setStyles() {
chrome.storage.local.get(null, function(obj){
if (obj.color1) $('#la-player-line').css('backgroundColor', 'rgba(' + hexToRgb(obj.color1) + ', .5)');
if (obj.color2) $('#la-player-line div').css('backgroundColor', obj.color2);
if (obj.height) $('#la-player-line').css('height', obj.height + 'px');
});
}
chrome.storage.onChanged.addListener(function(obj){
if (obj.color1 && obj.color1.newValue) $('#la-player-line').css('backgroundColor', 'rgba(' + hexToRgb(obj.color1.newValue) + ', .5)');
if (obj.color2 && obj.color2.newValue) $('#la-player-line div').css('backgroundColor', obj.color2.newValue);
if (obj.height && obj.height.newValue) $('#la-player-line').css('height', obj.height.newValue + 'px');
});
if (!$('body').attr('laYoutubeLine')) {
$('body').attr('laYoutubeLine', 'true');
$(document).ready(function(){
function wait() {
if ($('.ytp-live').length)
setTimeout(wait, 2000);
else {
$('#la-player-line').css('display', 'block');
setLine();
}
}
function setLine() {
if ($('.ytp-live').length) {
$('#la-player-line').css('display', 'none');
wait();
return false;
}
var block = $('video');
if (block.length) {
var proc = block[0].currentTime / block[0].duration;
$('#la-player-line div').css('width', (proc * 100) + '%');
}
setTimeout(setLine, 500);
}
function addLine() {
if ($('#movie_player').length) {
$('#movie_player').append('<div id="la-player-line"><div></div></div>');
setStyles();
setLine();
}
else setTimeout(addLine, 500);
}
addLine();
});
}
else setStyles();
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"><path style="fill:#58718C;" d="M306.087,439.652H16.696C7.479,439.652,0,432.174,0,422.957c0-9.217,7.479-16.696,16.696-16.696h289.391c9.217,0,16.696,7.479,16.696,16.696C322.783,432.174,315.304,439.652,306.087,439.652z"/><path style="fill:#53565C;" d="M306.087,406.261H256v33.391h50.087c9.217,0,16.696-7.479,16.696-16.696C322.783,413.739,315.304,406.261,306.087,406.261z"/><path style="fill:#E55639;" d="M461.913,339.478H50.087C22.468,339.478,0,317.01,0,289.391V89.043c0-27.619,22.468-50.087,50.087-50.087h411.826c27.619,0,50.087,22.468,50.087,50.087v200.348C512,317.01,489.532,339.478,461.913,339.478z"/><path style="fill:#CF4529;" d="M461.913,38.957H256v300.522h205.913c27.619,0,50.087-22.468,50.087-50.087V89.043C512,61.424,489.532,38.957,461.913,38.957z"/><path style="fill:#50667F;" d="M495.304,439.652H372.87c-9.217,0-16.696-7.479-16.696-16.696c0-9.217,7.479-16.696,16.696-16.696h122.435c9.217,0,16.696,7.479,16.696,16.696C512,432.174,504.521,439.652,495.304,439.652z"/><path style="fill:#E6EEFF;" d="M332.021,175.304L256,124.661l-24.153-16.139c-11.13-7.346-25.934,0.668-25.934,13.913V256c0,13.357,14.915,21.259,25.934,13.913L256,253.774l76.021-50.643C341.927,196.452,341.927,181.983,332.021,175.304z"/><path style="fill:#CF4529;" d="M339.478,473.043c-27.619,0-50.087-22.468-50.087-50.087c0-27.619,22.468-50.087,50.087-50.087s50.087,22.468,50.087,50.087C389.565,450.576,367.097,473.043,339.478,473.043z"/><path style="fill:#DAE2F2;" d="M332.021,203.13L256,253.774V124.661l76.021,50.643C341.927,181.983,341.927,196.452,332.021,203.13z"/><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+36
View File
@@ -0,0 +1,36 @@
Copyright JS Foundation and other contributors, https://js.foundation/
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.
+33
View File
@@ -0,0 +1,33 @@
{
"author": "Light Alex",
"background": {
"service_worker": "files/script/background.js"
},
"action": {
"default_icon": {
"16": "icons/16.png",
"24": "icons/24.png",
"32": "icons/32.png"
}
},
"content_scripts": [ {
"css": [ "files/css/style.css" ],
"js": [ "files/script/jquery-3.2.1.min.js", "files/script/postload.js" ],
"matches": [ "*://youtube.com/*", "*://www.youtube.com/*" ]
} ],
"default_locale": "en",
"description": "__MSG_description__",
"icons": {
"128": "icons/128.png",
"256": "icons/256.png",
"48": "icons/48.png",
"64": "icons/64.png"
},
"manifest_version": 3,
"name": "YouTube Line",
"options_page": "options.html",
"permissions": [ "scripting", "storage" ],
"host_permissions": [ "*://youtube.com/*", "*://www.youtube.com/*" ],
"short_name": "YouTube Line",
"version": "1.0.3.4"
}
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>YouTube Line Settings</title>
<link rel="stylesheet" href="files/css/options.css">
<script src="files/script/jquery-3.2.1.min.js"></script>
<script src="files/script/options.js"></script>
</head>
<body>
<div id="body">
<div id="cell">
<div id="block">
<h1>YouTube Line Settings</h1>
<div>
<span>Color #1</span>
<input type="color" name="color1" value="#808080">
</div>
<div>
<span>Color #2</span>
<input type="color" name="color2" value="#FF0000">
</div>
<div>
<span>Line height</span>
<input type="number" name="height" value="2" min="1" max="20" step="1">
</div>
<button>Save</button>
</div>
</div>
</div>
</body>
</html>
+272
View File
@@ -0,0 +1,272 @@
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
$ErrorActionPreference = 'Stop'
$Root = Split-Path -Parent $PSScriptRoot
$OutDir = Join-Path $Root 'store-assets'
$IconPath = Join-Path $Root '1.0.3.2_mv3\icons\128.png'
if (-not (Test-Path $OutDir)) {
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
}
function Color-Hex($hex) {
return [System.Drawing.ColorTranslator]::FromHtml($hex)
}
function New-Font($size, $style = [System.Drawing.FontStyle]::Regular) {
return New-Object System.Drawing.Font('Segoe UI', $size, $style, [System.Drawing.GraphicsUnit]::Pixel)
}
function New-Canvas($width, $height) {
$bitmap = New-Object System.Drawing.Bitmap($width, $height, [System.Drawing.Imaging.PixelFormat]::Format24bppRgb)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
$graphics.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::ClearTypeGridFit
$graphics.Clear((Color-Hex '#f4f6fb'))
return [pscustomobject]@{ Bitmap = $bitmap; Graphics = $graphics }
}
function New-RoundRectPath($x, $y, $w, $h, $r) {
$path = New-Object System.Drawing.Drawing2D.GraphicsPath
$d = $r * 2
$path.AddArc($x, $y, $d, $d, 180, 90)
$path.AddArc($x + $w - $d, $y, $d, $d, 270, 90)
$path.AddArc($x + $w - $d, $y + $h - $d, $d, $d, 0, 90)
$path.AddArc($x, $y + $h - $d, $d, $d, 90, 90)
$path.CloseFigure()
return $path
}
function Fill-RoundRect($g, $x, $y, $w, $h, $r, $color) {
$brush = New-Object System.Drawing.SolidBrush($color)
$path = New-RoundRectPath $x $y $w $h $r
$g.FillPath($brush, $path)
$path.Dispose()
$brush.Dispose()
}
function Draw-RoundRect($g, $x, $y, $w, $h, $r, $color, $width = 1) {
$pen = New-Object System.Drawing.Pen($color, $width)
$path = New-RoundRectPath $x $y $w $h $r
$g.DrawPath($pen, $path)
$path.Dispose()
$pen.Dispose()
}
function Draw-Text($g, $text, $x, $y, $size, $colorHex, $style = [System.Drawing.FontStyle]::Regular, $maxWidth = 0, $maxHeight = 0) {
$font = New-Font $size $style
$brush = New-Object System.Drawing.SolidBrush((Color-Hex $colorHex))
if ($maxWidth -gt 0) {
$rect = New-Object System.Drawing.RectangleF($x, $y, $maxWidth, $(if ($maxHeight -gt 0) { $maxHeight } else { 1000 }))
$format = New-Object System.Drawing.StringFormat
$format.Trimming = [System.Drawing.StringTrimming]::EllipsisWord
$g.DrawString($text, $font, $brush, $rect, $format)
$format.Dispose()
} else {
$g.DrawString($text, $font, $brush, [float]$x, [float]$y)
}
$brush.Dispose()
$font.Dispose()
}
function Draw-CenteredText($g, $text, $x, $y, $w, $h, $size, $colorHex, $style = [System.Drawing.FontStyle]::Regular) {
$font = New-Font $size $style
$brush = New-Object System.Drawing.SolidBrush((Color-Hex $colorHex))
$rect = New-Object System.Drawing.RectangleF($x, $y, $w, $h)
$format = New-Object System.Drawing.StringFormat
$format.Alignment = [System.Drawing.StringAlignment]::Center
$format.LineAlignment = [System.Drawing.StringAlignment]::Center
$g.DrawString($text, $font, $brush, $rect, $format)
$format.Dispose()
$brush.Dispose()
$font.Dispose()
}
function Draw-Header($g, $subtitle) {
Fill-RoundRect $g 64 48 1152 88 18 (Color-Hex '#ffffff')
Draw-RoundRect $g 64 48 1152 88 18 (Color-Hex '#dfe3ec') 1
if (Test-Path $IconPath) {
$icon = [System.Drawing.Image]::FromFile($IconPath)
$g.DrawImage($icon, 88, 66, 48, 48)
$icon.Dispose()
}
Draw-Text $g 'YouTube Line' 152 63 28 '#151922' ([System.Drawing.FontStyle]::Bold)
Draw-Text $g $subtitle 152 98 17 '#667085'
}
function Draw-Browser($g, $x, $y, $w, $h, $url = 'youtube.com/watch') {
Fill-RoundRect $g $x $y $w $h 16 (Color-Hex '#ffffff')
Draw-RoundRect $g $x $y $w $h 16 (Color-Hex '#d6dce8') 1
Fill-RoundRect $g ($x + 1) ($y + 1) ($w - 2) 56 16 (Color-Hex '#f0f3f8')
$dotColors = @('#ef4444', '#f59e0b', '#22c55e')
for ($i = 0; $i -lt 3; $i++) {
$brush = New-Object System.Drawing.SolidBrush((Color-Hex $dotColors[$i]))
$g.FillEllipse($brush, $x + 24 + ($i * 24), $y + 22, 12, 12)
$brush.Dispose()
}
Fill-RoundRect $g ($x + 118) ($y + 14) ($w - 154) 30 14 (Color-Hex '#ffffff')
Draw-Text $g $url ($x + 140) ($y + 19) 14 '#667085'
}
function Draw-Player($g, $x, $y, $w, $h, $progress, $lineColor, $showControls = $false) {
Fill-RoundRect $g $x $y $w $h 10 (Color-Hex '#111827')
$sky = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
(New-Object System.Drawing.Rectangle($x, $y, $w, $h)),
(Color-Hex '#172033'),
(Color-Hex '#05070c'),
90
)
$path = New-RoundRectPath $x $y $w $h 10
$g.FillPath($sky, $path)
$path.Dispose()
$sky.Dispose()
Fill-RoundRect $g ($x + 78) ($y + 82) ($w - 156) ($h - 188) 18 (Color-Hex '#1e293b')
Draw-CenteredText $g 'Video player' ($x + 78) ($y + 92) ($w - 156) 72 28 '#e5e7eb' ([System.Drawing.FontStyle]::Bold)
Draw-CenteredText $g 'Native controls hidden' ($x + 78) ($y + 145) ($w - 156) 38 17 '#9ca3af'
$barY = $y + $h - 18
Fill-RoundRect $g ($x + 24) $barY ($w - 48) 6 3 (Color-Hex '#9ca3af')
Fill-RoundRect $g ($x + 24) $barY ([int](($w - 48) * $progress)) 6 3 (Color-Hex $lineColor)
if ($showControls) {
Fill-RoundRect $g ($x + 30) ($y + $h - 74) ($w - 60) 44 10 (Color-Hex '#000000')
Draw-Text $g '0:42 / 4:15' ($x + 84) ($y + $h - 63) 17 '#ffffff'
Fill-RoundRect $g ($x + 44) ($y + $h - 58) 18 18 3 (Color-Hex '#ffffff')
}
}
function Draw-Badge($g, $x, $y, $text, $bgHex, $fgHex) {
$font = New-Font 16 ([System.Drawing.FontStyle]::Bold)
$size = $g.MeasureString($text, $font)
$w = [int]$size.Width + 34
$h = 34
Fill-RoundRect $g $x $y $w $h 17 (Color-Hex $bgHex)
Draw-CenteredText $g $text $x $y $w $h 16 $fgHex ([System.Drawing.FontStyle]::Bold)
$font.Dispose()
return $w
}
function Draw-OptionRow($g, $x, $y, $label, $valueHex) {
Draw-Text $g $label $x ($y + 8) 18 '#344054'
Fill-RoundRect $g ($x + 230) $y 112 40 8 (Color-Hex '#f9fafb')
Draw-RoundRect $g ($x + 230) $y 112 40 8 (Color-Hex '#d0d5dd') 1
Fill-RoundRect $g ($x + 244) ($y + 10) 20 20 4 (Color-Hex $valueHex)
Draw-Text $g $valueHex ($x + 274) ($y + 9) 15 '#344054'
}
function Save-Canvas($canvas, $path) {
$canvas.Bitmap.Save($path, [System.Drawing.Imaging.ImageFormat]::Png)
$canvas.Graphics.Dispose()
$canvas.Bitmap.Dispose()
}
function New-MainScreenshot {
$c = New-Canvas 1280 800
$g = $c.Graphics
Draw-Header $g 'A persistent progress line for the YouTube player'
Draw-Browser $g 96 170 1088 536
Draw-Player $g 140 252 790 394 0.43 '#ff2f2f' $false
Draw-Text $g 'Progress stays visible' 965 274 28 '#101828' ([System.Drawing.FontStyle]::Bold) 205 120
Draw-Text $g 'Keep track of video playback even when the native controls fade out.' 965 390 19 '#475467' ([System.Drawing.FontStyle]::Regular) 185 150
Draw-Badge $g 965 500 'Always on' '#fee4e2' '#b42318' | Out-Null
Draw-Badge $g 965 547 'Clean UI' '#e0f2fe' '#026aa2' | Out-Null
Draw-Badge $g 965 594 'YouTube only' '#ecfdf3' '#027a48' | Out-Null
Save-Canvas $c (Join-Path $OutDir 'youtube-line-screenshot-main-1280x800.png')
}
function New-OptionsScreenshot {
$c = New-Canvas 1280 800
$g = $c.Graphics
Draw-Header $g 'Customize the line color, progress color, and height'
Draw-Browser $g 96 170 1088 536 'chrome-extension://options'
Fill-RoundRect $g 165 244 420 376 18 (Color-Hex '#ffffff')
Draw-RoundRect $g 165 244 420 376 18 (Color-Hex '#d0d5dd') 1
Draw-Text $g 'YouTube Line Settings' 205 287 27 '#101828' ([System.Drawing.FontStyle]::Bold)
Draw-OptionRow $g 205 356 'Background color' '#808080'
Draw-OptionRow $g 205 424 'Progress color' '#ff0000'
Draw-Text $g 'Line height' 205 500 18 '#344054'
Fill-RoundRect $g 435 492 112 40 8 (Color-Hex '#f9fafb')
Draw-RoundRect $g 435 492 112 40 8 (Color-Hex '#d0d5dd') 1
Draw-CenteredText $g '2 px' 435 492 112 40 16 '#344054'
Fill-RoundRect $g 205 560 342 44 8 (Color-Hex '#ef4444')
Draw-CenteredText $g 'Save' 205 560 342 44 17 '#ffffff' ([System.Drawing.FontStyle]::Bold)
Draw-Player $g 650 272 445 282 0.62 '#ff0000' $false
Draw-Text $g 'Live preview' 650 582 22 '#101828' ([System.Drawing.FontStyle]::Bold)
Draw-Text $g 'Your settings are stored locally in Chrome.' 650 616 17 '#667085' ([System.Drawing.FontStyle]::Regular) 420
Save-Canvas $c (Join-Path $OutDir 'youtube-line-screenshot-options-1280x800.png')
}
function New-BeforeAfterScreenshot {
$c = New-Canvas 1280 800
$g = $c.Graphics
Draw-Header $g 'See playback progress without opening the controls'
Draw-Browser $g 96 170 1088 536
Draw-Text $g 'Without extension' 158 242 24 '#475467' ([System.Drawing.FontStyle]::Bold)
Draw-Text $g 'With YouTube Line' 676 242 24 '#101828' ([System.Drawing.FontStyle]::Bold)
Draw-Player $g 150 292 450 292 0.00 '#111827' $false
Fill-RoundRect $g 176 558 398 5 3 (Color-Hex '#111827')
Draw-CenteredText $g 'Progress is hidden with controls' 150 604 450 38 17 '#667085'
Draw-Player $g 668 292 450 292 0.47 '#ff2f2f' $false
Draw-CenteredText $g 'Progress remains visible' 668 604 450 38 17 '#667085'
$pen = New-Object System.Drawing.Pen((Color-Hex '#d0d5dd'), 2)
$g.DrawLine($pen, 640, 248, 640, 652)
$pen.Dispose()
Save-Canvas $c (Join-Path $OutDir 'youtube-line-screenshot-before-after-1280x800.png')
}
function New-SmallPromoTile {
$c = New-Canvas 440 280
$g = $c.Graphics
$g.Clear((Color-Hex '#111827'))
if (Test-Path $IconPath) {
$icon = [System.Drawing.Image]::FromFile($IconPath)
$g.DrawImage($icon, 28, 28, 56, 56)
$icon.Dispose()
}
Draw-Text $g 'YouTube Line' 104 30 30 '#ffffff' ([System.Drawing.FontStyle]::Bold)
Draw-Text $g 'Keep video progress visible' 104 70 17 '#cbd5e1'
Fill-RoundRect $g 28 168 384 8 4 (Color-Hex '#94a3b8')
Fill-RoundRect $g 28 168 224 8 4 (Color-Hex '#ff3030')
Fill-RoundRect $g 28 202 180 36 18 (Color-Hex '#fee4e2')
Draw-CenteredText $g 'Always visible' 28 202 180 36 16 '#b42318' ([System.Drawing.FontStyle]::Bold)
Save-Canvas $c (Join-Path $OutDir 'youtube-line-promo-small-440x280.png')
}
function New-MarqueePromoTile {
$c = New-Canvas 1400 560
$g = $c.Graphics
$g.Clear((Color-Hex '#101828'))
if (Test-Path $IconPath) {
$icon = [System.Drawing.Image]::FromFile($IconPath)
$g.DrawImage($icon, 90, 88, 96, 96)
$icon.Dispose()
}
Draw-Text $g 'YouTube Line' 220 84 56 '#ffffff' ([System.Drawing.FontStyle]::Bold)
Draw-Text $g 'Keep video progress visible when YouTube controls fade out.' 224 154 27 '#cbd5e1' ([System.Drawing.FontStyle]::Regular) 570
Draw-Badge $g 224 240 'Always visible' '#fee4e2' '#b42318' | Out-Null
Draw-Badge $g 420 240 'Customizable' '#e0f2fe' '#026aa2' | Out-Null
Draw-Badge $g 618 240 'YouTube only' '#ecfdf3' '#027a48' | Out-Null
Fill-RoundRect $g 90 390 610 10 5 (Color-Hex '#94a3b8')
Fill-RoundRect $g 90 390 356 10 5 (Color-Hex '#ff3030')
Draw-Browser $g 820 78 470 340
Draw-Player $g 850 158 410 230 0.57 '#ff3030' $false
Fill-RoundRect $g 850 450 410 44 22 (Color-Hex '#ffffff')
Draw-CenteredText $g 'Progress line stays visible' 850 450 410 44 20 '#101828' ([System.Drawing.FontStyle]::Bold)
Save-Canvas $c (Join-Path $OutDir 'youtube-line-promo-marquee-1400x560.png')
}
New-MainScreenshot
New-OptionsScreenshot
New-BeforeAfterScreenshot
New-SmallPromoTile
New-MarqueePromoTile
Get-ChildItem -LiteralPath $OutDir -Filter '*.png' | Sort-Object Name | Select-Object FullName, Length
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB