Code Monkey home page Code Monkey logo

browser-extensions's Introduction

browser-extensions's People

Contributors

alvinhkh avatar dotiful avatar maple3142 avatar marchagen avatar potpourri avatar vanja-san avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

browser-extensions's Issues

[Enhancement] Downloading VP9 or AV1 video and OPUS audio when available at 1080p (or lower) source

So far this wasn't an issue for me since I've always been using the manual link downloads, but the manual link downloads are now throttled since a few days while the one click download isn't, which is why I'm proposing this enhancement.
It would be nice to be able to get the higher quality codecs (when available) when the source video is 1080p (or lower) when using the one click download. The one click download only downloads AVC (x264) video and MP3 audio even when VP9 or AV1 video and OPUS audio is available. I tried editing the script by changing
x.mimeType.includes('video/mp4')
to
x.mimeType.includes('video/webm')
for both video and audio, and while the video is correctly downloaded in VP9, the audio is being downloaded in the lowest OPUS quality. This is (probably) because the script takes the first OPUS codec from the itag list , which is sorted in ascending order, and the highest quality OPUS audio is the highest ID from the list (in this case, itag=251).
My coding knowledge is rather limited so I'm not able to go much further by myself, but maybe the script can be made to take the highest itag from the audio and video list respectively (since this is also the case for video side, where the itags are in ascending order of AVC<VP9<AV1 within the same resolution and framerate), or to sort by descending order instead.
Note that the video is downloaded in VP9 when the source is >1080p (since such sources don't have AVC encoding)

Hindi Translation

hi: {
	togglelinks: 'लिंक टॉगल करें',
	stream: 'स्ट्रीमिंग (Stream)',
	adaptive: 'अनुकूली (Adaptive)',
        videoid: 'वीडियो आईडी: {{id}}'
}

No audio when downloading mp4

When choosing any mp4 option in the "adaptive" tab the video always lacks any sort of audio. The 360p option under "stream" works, but it's only 360p. How can I download 1080p with audio?

Spanish Translation

	es: {
		togglelinks: 'Mostrar/Ocultar Links',
		stream: 'Stream',
		adaptive: 'Adaptable',
		videoid: 'Id del Video: ',
		thumbnail: 'Miniatura',
		inbrowser_adaptive_merger: 'Acoplar Audio a Video '
	},

translate Korean

	kr: {
		togglelinks: '링크 보이기/숨기기',
		stream: '스트리밍',
		adaptive: '조정 가능한',
		videoid: 'Video Id: {{id}}'
	},

Translation to Hindi (hi)

Hindi Translation is below.

hi: {
togglelinks: 'लिंक दिखाएँ/छिपाएँ'
stream: 'स्ट्रीम'',
adaptive: 'अडैप्टिव (कोई आवाज नहीं)',
videoid: 'वीडियो आईडी: ',
inbrowser_adaptive_merger: 'ऑनलाइन एडैप्टिव वीडियो और ऑडियो मर्जर (FFmpeg)',
dlmp4: 'हाई-रिज़ॉल्यूशन mp4 एक क्लिक में डाउनलोड करें',
get_video_failed: 'अज्ञात कारण से वीडियो जानकारी प्राप्त करने में विफल, पेज रीफ्रेश करें काम कर सकता है।',
live_stream_disabled_message: 'स्थानीय YouTube डाउनलोडर लाइव स्ट्रीम के लिए उपलब्ध नहीं है''
}

parsedecsig error: TypeError: Cannot read properties of null (reading '0')

好久不見~
Error occurred at:

const helper = helperresult[0]

script content: https://www.youtube.com/s/player/11667490/player_ias.vflset/zh_HK/base.js

Some debug info:

Variable Value
fnname Xua
argname a
fnbody a=a.split("");MC["if"](a,71);MC.pn(a,3);MC.QO(a,27);MC.pn(a,2);MC["if"](a,36);MC.QO(a,14);MC.pn(a,3);MC.QO(a,68);MC.pn(a,3);return a.join("")
helpername MC["if"](a,71);MC

How come the minifier use if as a property name XD

So I managed to solve that by change helpernameresult to /;([a-zA-Z0-9$_]+?)\..+?\(/.exec(fnbody).
And for safety let's also add _ to fnnameresult, i.e. /=([a-zA-Z0-9$_]+?)\(decodeURIComponent/.exec(data).

Translation for Brazilian Portuguese (PT-BR)

Hi
I don't know how to use github properly but i want to help in the translation
There is the Brazilian Portuguese version from English Version

pt-br: {
togglelinks: 'Mostrar/Esconder Links',
stream: 'Stream',
adaptive: 'Adaptativo (Sem Som)',
videoid: 'ID do Vídeo: ',
inbrowser_adaptive_merger: 'Video Adaptativo Online & Mesclagem de Áudio (FFmpeg)',
dlmp4: 'Baixe MP4 em Alta Resolução Com um Clique!',
get_video_failed: 'Falha ao Pegar Dados do Vídeo por Erro Desconhecido, Tente Recarregar a Página.',
live_stream_disabled_message: 'Local YouTube Downloader Não Funciona Para Streams ao Vivo.'

Polish, Russian and Hungarian translations.

Here it is:

// ==UserScript==
// @name Local YouTube Downloader
// @name:zh-TW 本地 YouTube 下載器
// @name:zh-CN 本地 YouTube 下载器
// @namespace https://blog.maple3142.net/
// @Version 0.5.8
// @description Get youtube raw link without external service.
// @description:zh-TW 不需要透過第三方的服務就能下載 YouTube 影片。
// @description:zh-CN 不需要透过第三方的服务就能下载 YouTube 影片。
// @author maple3142
// @match https://.youtube.com/
// @connect youtube.com
// @require https://cdnjs.cloudflare.com/ajax/libs/hyperapp/1.2.6/hyperapp.js
// @run-at document-start
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

;(function() {
'use strict'
const DEBUG = false
const create$p = console =>
Object.keys(console)
.map(k => [k, (...args) => (DEBUG ? console[k]('YTDL: ' + args[0], ...args.slice(1)) : void 0)])
.reduce((acc, [k, fn]) => ((acc[k] = fn), acc), {})
const $p = create$p(console)

const LANG_FALLBACK = 'en'
const LOCALE = {
	en: {
		togglelinks: 'Show/Hide Links',
		stream: 'Stream',
		adaptive: 'Adaptive',
		videoid: 'Video Id: {{id}}'
	},
    pl:
	    togglelinks: 'Pokaż/ukryj linki',
		stream: 'Strumień',
		adaptive: 'Adaptacyjny',
		videoid: 'Id filmu: {{id}}'
	},
	ru:
	    togglelinks: 'Показать/скрыть ссылки',
		stream: 'Ручей',
		adaptive: 'Адаптивный',
		videoid: 'Id видео: {{id}}',
	},
	hu:
	    togglelinks: 'Linkek megjelenítése/elrejtése',
		stream: 'Folyam',
		adaptive: 'Alkalmazkodó',
		videoid: 'Videóazonosító: {{id}}',
	},	
	'zh-tw': {
		togglelinks: '顯示/隱藏連結',
		stream: '串流 Stream',
		adaptive: '自適應 Adaptive',
		videoid: '影片 Id: {{id}}'
	},
	zh: {
		togglelinks: '显示/隐藏连结',
		stream: '串流 Stream',
		adaptive: '自适应 Adaptive',
		videoid: '影片 Id: {{id}}'
	}
}
const findLang = l => {
	// language resolution logic: zh-tw --(if not exists)--> zh --(if not exists)--> LANG_FALLBACK(en)
	l = l.toLowerCase()
	if (l in LOCALE) return l
	else if (l.length > 2) return findLang(l.split('-')[0])
	else return LANG_FALLBACK
}

const format = s => d => s.replace(/{{(\w+?)}}/g, (m, g1) => d[g1])
const $ = (s, x = document) => x.querySelector(s)
const $el = (tag, opts) => {
	const el = document.createElement(tag)
	Object.assign(el, opts)
	return el
}
const gmxhr = o => new Promise((res, rej) => GM_xmlhttpRequest({ ...o, onload: res, onerror: rej }))
const xhrhead = url =>
	new Promise((res, rej) => {
		const xhr = new XMLHttpRequest()
		xhr.open('HEAD', url)
		xhr.onreadystatechange = () => {
			if (xhr.readyState === xhr.DONE) {
				res(xhr.responseText)
			}
		}
		xhr.onerror = rej
		xhr.send()
	})
const getytplayer = async () => {
	if (typeof ytplayer !== 'undefined' && ytplayer.config) return ytplayer
	$p.log('No ytplayer is founded')
	const html = await gmxhr({
		method: 'GET',
		url: 'https://www.youtube.com' + location.pathname + location.search
	}).then(r => r.responseText)
	const d = /<script >(var ytplayer[\s\S]*?)ytplayer\.load/.exec(html)
	let config = eval(d[1])
	unsafeWindow.ytplayer = {
		config
	}
	$p.log('ytplayer fetched: %o', unsafeWindow.ytplayer)
	return ytplayer
}
const parsedecsig = data => {
	const fnname = /\"signature\"\),.+?\.set\(.+?,(.+?)\(/.exec(data)[1]
	const [_, argname, fnbody] = new RegExp(fnname + '=function\\((.+?)\\){(.+?)}').exec(data)
	const helpername = /;(.+?)\..+?\(/.exec(fnbody)[1]
	const helper = new RegExp('var ' + helpername + '={[\\s\\S]+?};').exec(data)[0]
	return new Function([argname], helper + ';' + fnbody)
}
const getdecsig = path => xhrhead('https://www.youtube.com' + path).then(parsedecsig)
const parseQuery = s =>
	Object.assign(
		...s
			.split('&')
			.map(x => x.split('='))
			.map(p => ({ [p[0]]: decodeURIComponent(p[1]) }))
	)
const getVideo = async (id, decsig) => {
	return fetch(`https://www.youtube.com/get_video_info?video_id=${id}&el=detailpage`)
		.then(r => r.text())
		.then(async data => {
			const obj = parseQuery(data)
			$p.log(`video ${id} data: %o`, obj)
			if (obj.status === 'fail') {
				throw obj
			}
			let stream = []
			if (obj.url_encoded_fmt_stream_map) {
				stream = obj.url_encoded_fmt_stream_map.split(',').map(parseQuery)
				if (stream[0].sp && stream[0].sp.includes('signature')) {
					stream = stream
						.map(x => ({ ...x, s: decsig(x.s) }))
						.map(x => ({ ...x, url: x.url + `&signature=${x.s}` }))
				}
			}

			let adaptive = []
			if (obj.adaptive_fmts) {
				adaptive = obj.adaptive_fmts.split(',').map(parseQuery)
				if (adaptive[0].sp && adaptive[0].sp.includes('signature')) {
					adaptive = adaptive
						.map(x => ({ ...x, s: decsig(x.s) }))
						.map(x => ({ ...x, url: x.url + `&signature=${x.s}` }))
				}
			}
			$p.log(`video ${id} result: %o`, { stream, adaptive })
			return { stream, adaptive }
		})
}
const ytdlWorkerCode = `

const DEBUG=${DEBUG}
const $p=(${create$p.toString()})(console)
const parseQuery=${parseQuery.toString()}
const xhrhead=${xhrhead.toString()}
const parsedecsig=${parsedecsig.toString()}
const getdecsig=${getdecsig.toString()}
const getVideo=${getVideo.toString()}
self.onmessage=async e=>{
const decsig=await getdecsig(e.data.path)
const result=await getVideo(e.data.id,decsig)
postMessage(result)
} const ytdlWorker = new Worker(URL.createObjectURL(new Blob([ytdlWorkerCode]))) const workerGetVideo = (id, path) => { $p.log(workerGetVideo start: ${id} ${path}`)
return new Promise((res, rej) => {
const callback = e => {
ytdlWorker.removeEventListener('message', callback)
$p.log('workerGetVideo end: %o', e.data)
res(e.data)
}
ytdlWorker.addEventListener('message', callback)
ytdlWorker.postMessage({ id, path })
})
}

const { app, h } = hyperapp
const state = {
	hide: true,
	id: '',
	stream: [],
	adaptive: [],
	lang: findLang(navigator.language),
	strings: LOCALE[findLang(navigator.language)]
}
$p.log(`default language: ${state.lang}`)
const actions = {
	toggleHide: () => state => ({ hide: !state.hide }),
	setState: newstate => state => newstate,
	setLang: lang => state => {
		const target = findLang(lang)
		$p.log(`language change to: ${target}`)
		return {
			lang: target,
			strings: LOCALE[target]
		}
	},
	getState: () => state => state
}
const view = (state, actions) =>
	h('div', { id: 'ytdl-box' }, [
		h(
			'div',
			{ onclick: () => actions.toggleHide(), id: 'ytdl-box-toggle', className: 't-center' },
			state.strings.togglelinks
		),
		h('div', { className: state.hide ? 'hide' : '' }, [
			h('div', { className: 't-center fs-14px' }, format(state.strings.videoid, state)),
			h('div', { className: 'd-flex' }, [
				h(
					'div',
					{ className: 'f-1 of-h' },
					[h('div', { className: 't-center fs-14px' }, state.strings.stream)].concat(
						state.stream.map(x =>
							h(
								'a',
								{ href: x.url, title: x.type, target: '_blank', className: 'ytdl-link-btn' },
								x.quality || x.type
							)
						)
					)
				),
				h(
					'div',
					{ className: 'f-1 of-h' },
					[h('div', { className: 't-center fs-14px' }, state.strings.adaptive)].concat(
						state.adaptive.map(x =>
							h(
								'a',
								{ href: x.url, title: x.type, target: '_blank', className: 'ytdl-link-btn' },
								(x.quality_label ? x.quality_label + ':' : '') + x.type
							)
						)
					)
				)
			])
		])
	])
const container = $el('div')
const $app = app(state, actions, view, container)
if (DEBUG) unsafeWindow.$app = $app
const load = async id => {
	const ytplayer = await getytplayer()
	return workerGetVideo(id, ytplayer.config.assets.js)
		.then(data => {
			$p.log('video loaded: %s', id)
			$app.setState({
				id,
				stream: data.stream,
				adaptive: data.adaptive
			})
			if (ytplayer.config.args.host_language) $app.setLang(ytplayer.config.args.host_language)
		})
		.catch(err => $p.error('load', err))
}
let prevurl = null
setInterval(() => {
	const el = $('#info-contents') || $('#watch-header') || $('ytm-item-section-renderer>lazy-list')
	if (el && !el.contains(container)) el.appendChild(container)
	if (location.href !== prevurl && location.pathname === '/watch') {
		prevurl = location.href
		$app.setState({
			hide: true
		})
		const id = new URLSearchParams(location.search).get('v')
		$p.log(`start loading new video: ${id}`)
		load(id)
	}
}, 1000)
GM_addStyle(`

.hide{
display: none;
}
.t-center{
text-align: center;
}
.d-flex{
display: flex;
}
.f-1{
flex: 1;
}
.fs-14px{
font-size: 14px;
}
.of-h{
overflow: hidden;
}
#ytdl-box{
border-bottom: 1px solid var(--yt-border-color);
}
#ytdl-box-toggle{
margin: 3px;
user-select: none;
-moz-user-select: -moz-none;
}
#ytdl-box-toggle:hover{
color: blue;
}
.ytdl-link-btn{
display: block;
border: 1px solid !important;
border-radius: 3px;
text-decoration: none !important;
outline: 0;
text-align: center;
padding: 2px;
margin: 5px;
color: black;
}
a.ytdl-link-btn{
text-decoration: none;
}
a.ytdl-link-btn:hover{
color: blue;
}
`)
})()

Local YouTube Downloader Czech translation

Here is a Czech translation for Local YouTube Downloader:
// @description:cs Stahujte YouTube videa bez externích služeb.

cs: {
			togglelinks: 'Zobrazit/Skrýt odkazy',
			stream: 'Stream',
			adaptive: 'Adaptivní',
			videoid: 'ID videa: ',
			inbrowser_adaptive_merger: 'Online nástroj pro sloučení videa a audia (FFmpeg)',
			dlmp4: 'Stáhnout video mp4 jedním kliknutím ve vysokém rozlišení',
			get_video_failed: 'Nepodařilo se nahrát informace o videu. Zkuste obnovit stránku (F5).',
			live_stream_disabled_message: 'Local YouTube Downloader není dostupný pro živé vysílání'
}

Here is a French translation for Local YouTube Downloader

// @description:fr Obtenez un lien brut YouTube sans service externe.

fr: {
	togglelinks: 'Afficher/Masquer les liens',
	stream: 'Stream',
	adaptive: 'Adaptative',
	videoid: 'ID vidéo: ',
	inbrowser_adaptive_merger:
		'Fusionner vidéos et audios adaptatifs dans le navigateur (FFmpeg)',
	dlmp4: 'Téléchargez la plus haute résolution mp4 en un clic',
	get_video_failed:
		'Il semble qu'une extension de blocage de pubs soit installée, ce qui bloque %s.\nVeuillez ajouter la règle suivante au jeu de règles, ou cela empêchera Local YouTube Downloader de fonctionner.\n\nPS: Si votre bloqueur refuse d'ajouter cette règle, vous devez le désinstaller et utiliser plutôt "uBlock Origin".\nSi vous ne comprenez toujours pas ce que je dis, désinstallez ou désactivez simplement votre bloqueur de pubs ...'
}

Suggestion

I have some suggestions "on the Local YouTube video downloader", like adding a green Download button next to the subscribe channel button, language change and making a better Download UI. The UI could look like this, the download button instead of save to album or next to the subscribe button with an option to choose between audio and the video format with the quality.
tamper monkey github youtube downloader suggestion 1

My professional Paint skills

Polish translation for Local YouTube Downloader

pl: {
	togglelinks: 'Pokaż/Ukryj Linki',
	stream: 'Stream',
	adaptive: 'Adaptywne',
	videoid: 'ID filmu: ',
	inbrowser_adaptive_merger:
		'Połącz audio i wideo adaptywne w przeglądarce (FFmpeg)',
	dlmp4: 'Pobierz .mp4 w najwyższej jakości'
}

Turkish Translation

Turkish translation is below:

tr: {
togglelinks: 'Linkleri Göster/Gizle',
stream: 'Yayın',
adaptive: 'Adaptif (Sessiz)',
videoid: 'Video ID: ',
inbrowser_adaptive_merger: 'Online Adaptif Video & Ses Birleştirici (FFmpeg)',
dlmp4: 'En yüksek çözünürlükte MP4 indir',
get_video_failed: 'Bilinmeyen bir nedenden dolayı video bilgileri alınamadı, sayfayı yenilemek işe yarayabilir.',
live_stream_disabled_message: 'Yerel Youtube Yükleyici canlı yayınlarda çalışmıyor'
},

Other Translations:
// @name:tr Yerel Youtube Yükleyici
// @description:tr Harici bir programa gerek kalmadan Youtube videoları indir.

Hebrew Translation

not sure if "iw" or "he"

xx : {
togglelinks: 'הצג/הסתר קישורים',
stream: 'סטרים',
adaptive: 'אדפטיבי',
videoid: 'מזהה סרטון: {{id}}'
},

[Suggestion] (Local Youtube Downloader) Generate video(s) without the sponsorships

It's a long shot I know, but there is already an extension out there doing a pretty great job at skipping ADs and other such stuff that video creators include in their videos. Being able to download videos from YouTube is great, but wouldn't you say it could be better if you could download some without the sponsors included?

I suggest looking into maybe using some SponsorBlock APIs (https://github.com/ajayyy/SponsorBlock) as it would prove most useful for what I imagine most of the user base is for the script you provide.

[Bug] Local YouTube Downloader can't download when use "Download high-resolution mp4 in one click"

本地 YouTube 下載器 點「一鍵下載高畫質 mp4」,出現的視窗下載進度永遠是0,等了10分鐘都沒動作。

「串流 Stream」和「自適應 Adaptive (沒有聲音)」可以下載,但是「串流 Stream」只有360p,「自適應 Adaptive (沒有聲音)」要手動下載影片和聲音再手動合併。希望您能修復一鍵下載高畫質的功能,感謝。

版本:
Windows 10
Chrome 120.0.6099.225
UserScript 0.9.55


附上截圖

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.