├── tests ├── __init__.py ├── files │ └── latimes.jpg ├── test_extract.py ├── test_adstxt.py ├── test_hyperlinks.py ├── test_accessibility.py ├── test_screenshot.py ├── test_robotstxt.py └── test_archive.py ├── newshomepages ├── __init__.py ├── extensions │ ├── adguard │ │ ├── web-accessible-resources │ │ │ └── redirects │ │ │ │ ├── noopcss.css │ │ │ │ ├── noopjson.json │ │ │ │ ├── nooptext.js │ │ │ │ ├── noopjs.js │ │ │ │ ├── noopvast02.xml │ │ │ │ ├── noopvast03.xml │ │ │ │ ├── noopvast04.xml │ │ │ │ ├── noopvmap01.xml │ │ │ │ ├── noopframe.html │ │ │ │ ├── noopmp3.mp3 │ │ │ │ ├── noopmp4.mp4 │ │ │ │ ├── 1x1-transparent.gif │ │ │ │ ├── 2x2-transparent.png │ │ │ │ ├── 3x2-transparent.png │ │ │ │ ├── 32x32-transparent.png │ │ │ │ ├── prebid-ads.js │ │ │ │ ├── noeval.js │ │ │ │ ├── naver-wcslog.js │ │ │ │ └── scorecardresearch-beacon.js │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── main.css │ │ │ │ ├── nanobar.css │ │ │ │ ├── fonts.css │ │ │ │ ├── devtools │ │ │ │ │ └── custom.css │ │ │ │ └── layout.css │ │ │ ├── icons │ │ │ │ ├── gray-19.png │ │ │ │ ├── gray-38.png │ │ │ │ ├── b13-on-19.png │ │ │ │ ├── b13-on-38.png │ │ │ │ ├── green-128.png │ │ │ │ ├── green-16.png │ │ │ │ ├── green-19.png │ │ │ │ ├── green-38.png │ │ │ │ ├── b13-off-19.png │ │ │ │ └── b13-off-38.png │ │ │ ├── images │ │ │ │ ├── favicon.ico │ │ │ │ ├── link.svg │ │ │ │ ├── shield.svg │ │ │ │ ├── trash.svg │ │ │ │ ├── reload-ico-green.svg │ │ │ │ ├── cross.svg │ │ │ │ ├── arrow-down.svg │ │ │ │ ├── tick.svg │ │ │ │ ├── arrow-down-grey.svg │ │ │ │ ├── checked.svg │ │ │ │ ├── dropbox.svg │ │ │ │ ├── toggler-bg.svg │ │ │ │ ├── alert.svg │ │ │ │ ├── logo-shield.svg │ │ │ │ ├── reload-ico.svg │ │ │ │ └── chrome.svg │ │ │ ├── fonts │ │ │ │ ├── Roboto-Bold.woff2 │ │ │ │ ├── Roboto-Medium.woff2 │ │ │ │ └── Roboto-Regular.woff2 │ │ │ └── js │ │ │ │ └── preload-theme.js │ │ ├── pages │ │ │ ├── background.html │ │ │ ├── devtools.html │ │ │ ├── popup.html │ │ │ ├── ad-blocked.html │ │ │ ├── filtering-log.html │ │ │ ├── safebrowsing.html │ │ │ ├── options.html │ │ │ ├── filter-download.html │ │ │ ├── fullscreen-user-rules.html │ │ │ ├── devtools.js │ │ │ └── devtools-elements-sidebar.html │ │ ├── _locales │ │ │ ├── bn │ │ │ │ └── messages.json │ │ │ ├── kn │ │ │ │ └── messages.json │ │ │ ├── lv │ │ │ │ └── messages.json │ │ │ ├── sr │ │ │ │ └── messages.json │ │ │ ├── ta │ │ │ │ └── messages.json │ │ │ ├── te │ │ │ │ └── messages.json │ │ │ ├── fil │ │ │ │ └── messages.json │ │ │ └── mk-MK │ │ │ │ └── messages.json │ │ └── manifest.json │ └── adguardextra │ │ ├── assets │ │ └── images │ │ │ ├── extra_logo.png │ │ │ ├── extra_icon_16.png │ │ │ ├── extra_icon_19.png │ │ │ ├── extra_icon_38.png │ │ │ ├── extra_icon_128.png │ │ │ └── extra_installed.png │ │ ├── _locales │ │ ├── ar │ │ │ └── messages.json │ │ ├── bg │ │ │ └── messages.json │ │ ├── et │ │ │ └── messages.json │ │ ├── fa │ │ │ └── messages.json │ │ ├── hy │ │ │ └── messages.json │ │ ├── sv │ │ │ └── messages.json │ │ ├── zh │ │ │ └── messages.json │ │ ├── ja │ │ │ └── messages.json │ │ ├── zh-TW │ │ │ └── messages.json │ │ ├── ko │ │ │ └── messages.json │ │ ├── he │ │ │ └── messages.json │ │ ├── da │ │ │ └── messages.json │ │ ├── cs │ │ │ └── messages.json │ │ ├── en │ │ │ └── messages.json │ │ ├── hr │ │ │ └── messages.json │ │ ├── nl │ │ │ └── messages.json │ │ ├── ro │ │ │ └── messages.json │ │ ├── sk │ │ │ └── messages.json │ │ ├── fi │ │ │ └── messages.json │ │ ├── no │ │ │ └── messages.json │ │ ├── sr │ │ │ └── messages.json │ │ ├── lt │ │ │ └── messages.json │ │ ├── uk │ │ │ └── messages.json │ │ ├── id │ │ │ └── messages.json │ │ ├── sl │ │ │ └── messages.json │ │ ├── pt │ │ │ └── messages.json │ │ ├── be │ │ │ └── messages.json │ │ ├── pt-PT │ │ │ └── messages.json │ │ ├── de │ │ │ └── messages.json │ │ ├── es │ │ │ └── messages.json │ │ ├── it │ │ │ └── messages.json │ │ ├── pl │ │ │ └── messages.json │ │ ├── ru │ │ │ └── messages.json │ │ ├── tr │ │ │ └── messages.json │ │ ├── fr │ │ │ └── messages.json │ │ ├── hu │ │ │ └── messages.json │ │ └── vi │ │ │ └── messages.json │ │ ├── popup.css │ │ ├── popup.html │ │ ├── content-script.js │ │ └── options.js ├── site │ ├── __init__.py │ ├── __main__.py │ ├── source_list.py │ ├── bundle_list.py │ ├── latest_screenshots.py │ ├── language_list.py │ ├── status_report.py │ ├── country_list.py │ ├── country_detail.py │ ├── language_detail.py │ ├── bundle_detail.py │ ├── cli.py │ └── performance_ranking.py ├── sources │ └── javascript │ │ ├── lavozdegalicia.js │ │ ├── asahi.js │ │ ├── fiquemsabendo.js │ │ ├── nikkei.js │ │ ├── sankei_news.js │ │ ├── newshour.js │ │ ├── sputnikint.js │ │ ├── bariweiss.js │ │ ├── le_figaro.js │ │ ├── maroelamedia.js │ │ ├── mediapart.js │ │ ├── platformer.js │ │ ├── wonkette.js │ │ ├── adnkronos.js │ │ ├── dallasnews.js │ │ ├── diariope.js │ │ ├── examinationnews.js │ │ ├── ilmanifesto.js │ │ ├── jessicavalenti.js │ │ ├── lanacion.js │ │ ├── opensecretsdc.js │ │ ├── realdailywire.js │ │ ├── seikyoofficial.js │ │ ├── thedispatch.js │ │ ├── 404mediaco.js │ │ ├── bonginoreport.js │ │ ├── capradionews.js │ │ ├── daily_record.js │ │ ├── eltiempo.js │ │ ├── fortunemagazine.js │ │ ├── georgiastraight.js │ │ ├── globalnews.js │ │ ├── izvestia_ru.js │ │ ├── kcautv.js │ │ ├── kpbs.js │ │ ├── kpcc.js │ │ ├── mtnstspotlight.js │ │ ├── spotlightpa.js │ │ ├── theathletic.js │ │ ├── thebabylonbee.js │ │ ├── theintercept.js │ │ ├── thesun.js │ │ ├── washingtonpost.js │ │ ├── zerohora.js │ │ ├── 20minutes.js │ │ ├── baltimorebanner.js │ │ ├── bbc.js │ │ ├── bloombergjapan.js │ │ ├── chess24com.js │ │ ├── drudge.js │ │ ├── firstthingsmag.js │ │ ├── humanevents.js │ │ ├── jdemontreal.js │ │ ├── le_parisien.js │ │ ├── motherjones.js │ │ ├── openvallejo.js │ │ ├── votebeatus.js │ │ ├── foxnews.js │ │ ├── globeandmail.js │ │ ├── harpers.js │ │ ├── laist.js │ │ ├── nybooks.js │ │ ├── propublica.js │ │ ├── sjvwater.js │ │ ├── uvaldenews.js │ │ ├── lastampa.js │ │ ├── lp_lapresse.js │ │ ├── publicintegrity.js │ │ ├── arthasarokar.js │ │ ├── franceinfo.js │ │ ├── gazettedotcom.js │ │ ├── gfherald.js │ │ ├── kccinews.js │ │ ├── occrp.js │ │ ├── repubblica.js │ │ ├── lobs.js │ │ ├── sdut.js │ │ ├── tribunedelyon.js │ │ ├── blaw.js │ │ ├── breitbartnews.js │ │ ├── dmregister.js │ │ ├── oann.js │ │ ├── postcrescent.js │ │ ├── presscitizen.js │ │ ├── teamtrace.js │ │ ├── crucessunnews.js │ │ ├── dailycaller.js │ │ ├── gbpressgazette.js │ │ ├── independent.js │ │ ├── jornaloglobo.js │ │ ├── journalsentinel.js │ │ ├── larazon_es.js │ │ ├── nationalpost.js │ │ ├── stevenspointjrl.js │ │ ├── cbcnews.js │ │ ├── mtlgazette.js │ │ ├── nbcnews.js │ │ ├── theriotimes.js │ │ ├── calgaryherald.js │ │ ├── financialpost.js │ │ ├── htrnews.js │ │ ├── nhk_news.js │ │ ├── ottawacitizen.js │ │ ├── sahanjournal.js │ │ ├── sowetanlive.js │ │ ├── startribune.js │ │ ├── thetorontosun.js │ │ ├── timeslive.js │ │ ├── vancouversun.js │ │ ├── vryeweekblad.js │ │ ├── andscape.js │ │ ├── dailymirror.js │ │ ├── ft.js │ │ ├── gridnews.js │ │ ├── sfchronicle.js │ │ ├── theinterceptbr.js │ │ ├── rfi.js │ │ ├── theblaze.js │ │ ├── theeconomist.js │ │ ├── theonion.js │ │ ├── torontostar.js │ │ ├── lehuffpost.js │ │ ├── qctimes.js │ │ ├── thedbk.js │ │ ├── baltimoremag.js │ │ ├── wcfcourier.js │ │ ├── ledevoir.js │ │ ├── elcorreo_com.js │ │ ├── vcstar.js │ │ ├── nytimes.js │ │ ├── france24.js │ │ ├── frednewspost.js │ │ ├── lajornada.js │ │ ├── lemonde_en.js │ │ ├── lemondefr.js │ │ ├── france24_en.js │ │ ├── baltimoresun.js │ │ ├── telegraph.js │ │ ├── thephilacitizen.js │ │ ├── abc_es.js │ │ ├── guardian.js │ │ ├── libe.js │ │ ├── cnn.js │ │ ├── corriere.js │ │ ├── pajaropolitico.js │ │ ├── pioneerpress.js │ │ ├── statnews.js │ │ ├── thetriibe.js │ │ ├── thewrap.js │ │ ├── usatoday.js │ │ ├── estadao.js │ │ ├── el_universal_mx.js │ │ ├── moreperfectus.js │ │ ├── ntdaily.js │ │ ├── latimes.js │ │ ├── chicagotribune.js │ │ ├── msnbc.js │ │ ├── pulitzercenter.js │ │ ├── civilbeat.js │ │ ├── thehilltimes.js │ │ ├── thetimes.js │ │ └── news_letter.js ├── analyze │ ├── __init__.py │ ├── __main__.py │ └── cli.py ├── extract │ ├── __init__.py │ ├── __main__.py │ ├── cli.py │ ├── utils.py │ ├── items.py │ ├── wayback.py │ └── accessibility.py ├── batch.py ├── accessibility.py └── adstxt.py ├── .flake8 ├── mypy.ini ├── .github ├── FUNDING.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── report-a-broken-site.md │ └── add-a-site.yaml └── workflows │ └── site.yml ├── _site ├── _static │ ├── img │ │ └── slack-baltimore-sun.png │ └── custom.css ├── _templates │ ├── bundles.md.tmpl │ ├── languages.md.tmpl │ ├── sources.md.tmpl │ ├── drudge-top-words.svg.tmpl │ ├── latest.md.tmpl │ ├── countries.md.tmpl │ ├── language_detail.md.tmpl │ ├── country_detail.md.tmpl │ ├── bundle_detail.md.tmpl │ └── site_detail.md.tmpl ├── Makefile ├── conf.py ├── gettingstarted.md ├── reference.rst └── index.md ├── SECURITY.md ├── CONTRIBUTING.md ├── MANIFEST.in ├── README.md ├── Pipfile └── .pre-commit-config.yaml /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | extend-ignore = D100,D104,E203,E501 3 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopcss.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopjson.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/nooptext.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | ignore_missing_imports = True 3 | exclude = newshomepages/extensions 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: palewire 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopjs.js: -------------------------------------------------------------------------------- 1 | (function() {})() -------------------------------------------------------------------------------- /newshomepages/site/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group as cli 2 | 3 | __all__ = ("cli",) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lavozdegalicia.js: -------------------------------------------------------------------------------- 1 | document.body.classList.remove("blocked"); 2 | -------------------------------------------------------------------------------- /newshomepages/analyze/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group as cli 2 | 3 | __all__ = ("cli",) 4 | -------------------------------------------------------------------------------- /newshomepages/extract/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group as cli 2 | 3 | __all__ = ("cli",) 4 | -------------------------------------------------------------------------------- /tests/files/latimes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/tests/files/latimes.jpg -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopvast02.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopvast03.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopvast04.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/site/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group 2 | 3 | if __name__ == "__main__": 4 | cli_group() 5 | -------------------------------------------------------------------------------- /newshomepages/analyze/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group 2 | 3 | if __name__ == "__main__": 4 | cli_group() 5 | -------------------------------------------------------------------------------- /newshomepages/extract/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli_group 2 | 3 | if __name__ == "__main__": 4 | cli_group() 5 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/asahi.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#befb-cmp-dialog').forEach((e) => e.remove()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/fiquemsabendo.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.maybe-later').forEach((e) => e.click()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nikkei.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.modalStyle_modrqv7').forEach((e) => e.remove()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sankei_news.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.CookieConsent').forEach((e) => e.remove()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/newshour.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#pbs-confirm-station').forEach((e) => e.remove()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sputnikint.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#iubenda-cs-banner').forEach((e) => e.remove()); 2 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/bariweiss.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.maybe-later' 3 | ).forEach((e) => e.click()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/le_figaro.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#appconsent' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/maroelamedia.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#mm-nb-1' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/mediapart.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#js-cc-modal').forEach( 2 | el => el.remove() 3 | ) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/platformer.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.maybe-later' 3 | ).forEach((e) => e.click()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/wonkette.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.maybe-later' 3 | ).forEach((e) => e.click()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/adnkronos.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.qc-cmp2-container' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/dallasnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".dmn-overlay" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/diariope.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.wpcc-container').forEach( 2 | el => el.remove() 3 | ) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/examinationnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.bg-grey-300' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/ilmanifesto.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".info-banner" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/jessicavalenti.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.maybe-later' 3 | ).forEach((e) => e.click()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lanacion.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.ln-banner-container' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/opensecretsdc.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.Modal,#Modal' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/realdailywire.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#wisepops-root' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/seikyoofficial.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#cookiePopUp').forEach( 2 | el => el.remove() 3 | ) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thedispatch.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.maybe-later').forEach((e) => { 2 | e.click() 3 | }) 4 | -------------------------------------------------------------------------------- /_site/_static/img/slack-baltimore-sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/_site/_static/img/slack-baltimore-sun.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/404mediaco.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.popup,.popup-content' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/bonginoreport.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#onetrust-consent-sdk' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/capradionews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | "#banner-pushdown" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/daily_record.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.qc-cmp-cleanslate' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/eltiempo.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.barra-aviso-privacidad' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/fortunemagazine.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#Leaderboard0').forEach( 2 | el => el.remove() 3 | ) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/georgiastraight.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.slide-down' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/globalnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.slide-down,.ads' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/izvestia_ru.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.partner_before_head' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/kcautv.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.js-banner' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/kpbs.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | 'ps-google-dfp-ad' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/kpcc.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.CampaignType--popup,.AdModule' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/mtnstspotlight.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.newspack-lightbox' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/spotlightpa.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.overflow-y-scroll' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theathletic.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gieWyh,.sc-9e0fc4d0-2' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thebabylonbee.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.overscroll-contain').forEach( 2 | el => el.remove() 3 | ) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theintercept.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".tp-container-inner" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thesun.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.billboard' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/washingtonpost.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.paywall-overlay' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/zerohora.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.overlay,.page-last' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/20minutes.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#didomi-popup,.ad-toppage' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/baltimorebanner.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.tp-modal,.tp-backdrop' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/bbc.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.nw-c-advert,.nw-c-leaderboard-ad' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/bloombergjapan.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.leaderboard-container' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/chess24com.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.background,.bubble.modal' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/drudge.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '[id^="div-gpt-ad"],#dr_consent' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/firstthingsmag.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#cboxOverlay,#colorbox' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/humanevents.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".grid-modal,#grid-modal" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/jdemontreal.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.piano_auth_wrapper' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/le_parisien.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#didomi-popup,.ad_element' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/motherjones.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.mj-adblock-widget,#ticker' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/openvallejo.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.mc-modal,.mc-modal-bg' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/votebeatus.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.Form-newsletter-breaker-bg' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "fonts.css"; 2 | @import "layout.css"; 3 | @import "style.css"; 4 | @import "log.css"; 5 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/foxnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.pre-content,.pf-widget,.fc-ab-root' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/globeandmail.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#subscription-pencil-area' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/harpers.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".cta-popup,#ctas,.modal-backdrop" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/laist.js: -------------------------------------------------------------------------------- 1 | // ad campaign for LAist 2 | document.querySelectorAll(".bankston-campaign").forEach((e) => e.remove()) 3 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nybooks.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.sumo-form-wrapper,#homepage-top-ad' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/propublica.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.syndicated-modal' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sjvwater.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.dialog-widget,#dialog-widget' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/uvaldenews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sgcolorbox,#sgcboxOverlay' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lastampa.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.iubenda-cs-banner,#iubenda-cs-banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lp_lapresse.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.homeHeadlinesRow__adWrapper' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/publicintegrity.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.newspack-lightbox' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/background.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/arthasarokar.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#esn_road_block_ad-8,.esn_road_block_ad' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/franceinfo.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#didomi-popup,#newsletterSubContainer' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/gazettedotcom.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.modal-scrollable,.connext-modal-backdrop' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/gfherald.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".modal-scrollable,.connext-modal-backdrop" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/kccinews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.ab-iam-root,.listing-page-ad' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/occrp.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.occrp-cookie-overlay,.cookie,#mc_embed_signup' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/repubblica.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#iubenda-cs-banner,.iubenda-cs-banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopvmap01.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lobs.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gdpr-glm-wall,#gdpr-glm-wall,.event-notification' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sdut.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '[data-ad-rendered],#ensNotifyBanner' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/tribunedelyon.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".cookie-law-info-bar,#cookie-law-info-bar" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/gray-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/gray-19.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/gray-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/gray-38.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/b13-on-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/b13-on-19.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/b13-on-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/b13-on-38.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/green-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/green-128.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/green-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/green-16.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/green-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/green-19.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/green-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/green-38.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/images/favicon.ico -------------------------------------------------------------------------------- /newshomepages/sources/javascript/blaw.js: -------------------------------------------------------------------------------- 1 | // Scroll down 1000 pixels 2 | window.scrollBy(0, 1000); 3 | 4 | // Scroll back up to the top of the page 5 | window.scrollTo(0, 0); 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/breitbartnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#onetrust-consent-sdk,#HaDW,#ISCTO_overlay,#ISCTO_div' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/dmregister.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/oann.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.cc_dialog,#IL_INSEARCH,.PD_modal_container,.PD_modal_overlay' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/postcrescent.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/presscitizen.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/teamtrace.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.js-donation-banner,.brocton-campaign,.CampaignType--popup' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/b13-off-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/b13-off-19.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/icons/b13-off-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/icons/b13-off-38.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/crucessunnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/dailycaller.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.adcontainer,#unit,.dc-sticky,#onesignal-slidedown-container' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/gbpressgazette.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/independent.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#ad_unit,#footerPrompt,.tp-modal,.tp-backdrop' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/jornaloglobo.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.tp-container-inner,.mobiliarioNaoBarreira,.mobiliarioAdblock' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/journalsentinel.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/larazon_es.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.sibbo-layout,#__blockNavigationOverlay,.advertising,.henneoHB' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nationalpost.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/stevenspointjrl.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gnt_mol_oy,.gnt_x__hi,#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /newshomepages/sources/javascript/cbcnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.privacyNotification,.bigBoxContainer,.ad-risingstar-container' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/mtlgazette.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nbcnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.header-and-footer--banner-ad,.notification-soft-optin' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theriotimes.js: -------------------------------------------------------------------------------- 1 | // Scroll down 1000 pixels 2 | window.scrollBy(0, 1000); 3 | 4 | // Scroll back up to the top of the page 5 | window.scrollTo(0, 0); 6 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/fonts/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/fonts/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/assets/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_logo.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/calgaryherald.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/financialpost.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/htrnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#gnt_mol_oy,[aria-label="advertisement"],#onetrust-banner-sdk' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nhk_news.js: -------------------------------------------------------------------------------- 1 | document.documentElement.style.overflowY = 'hidden' 2 | 3 | document.querySelectorAll('#bottom_optout_announce').forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/ottawacitizen.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sahanjournal.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.newspack-popup,.newspack_global_ad global_above_header' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sowetanlive.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#register-popup-backdrop, #register-popup-modal, #gdpr-overlay, .viafoura' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/startribune.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#overlay-main-container,.overlay-main-container,.ad-mod' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thetorontosun.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/timeslive.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#register-popup-backdrop, #register-popup-modal, #gdpr-overlay, .viafoura' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/vancouversun.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay,.consent__banner' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_icon_16.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_icon_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_icon_19.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_icon_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_icon_38.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/vryeweekblad.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#register-popup-backdrop, #register-popup-modal, #gdpr-overlay, .viafoura' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_icon_128.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/assets/images/extra_installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguardextra/assets/images/extra_installed.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/andscape.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#popup-interstitial-dialog,#dialogContainer,#overlayContainer,#attentive_overlay' 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/dailymirror.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#takeover,.qc-cmp2-container,#qc-cmp2-container,.ad-placeholder,#ad-placeholder' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/ft.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.o-cookie-message,.o-ads,.o-message__container,.n-feedback__survey-trigger' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/gridnews.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#onetrust-consent-sdk,.falkner-campaign,.CampaignType--popup,.wapella-campaign' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/sfchronicle.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.fancybox-overlay.fancybox-overlay-fixed,.belowMastheadWrapper' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theinterceptbr.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".tp-container-inner,.sgpb-popup-overlay,#sgpb-popup-dialog-main-div-wrapper" 3 | ).forEach((e) => e.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopmp3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/noopmp3.mp3 -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noopmp4.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/noopmp4.mp4 -------------------------------------------------------------------------------- /newshomepages/sources/javascript/rfi.js: -------------------------------------------------------------------------------- 1 | document.getElementsByTagName('body')[0].classList.remove("didomi-popup-open") 2 | 3 | document.querySelectorAll( 4 | '#didomi-host' 5 | ).forEach(el => el.remove()) 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theblaze.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sailthru-overlay-container,.sailthru-overlay-container,[id^="sFooter_"],#push-overlay,.overlay-bg' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theeconomist.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | 'div[id^="sp_message_container"],#sp_message_container_658357,.advert,#piano-ribbon' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/theonion.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.js_modal_exit_intent,.js_sticky-top-ad,.js_welcome-ad-mobile--container,.ad-container' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/torontostar.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.c-article-list-ad,.leaderboard-ad-component,.c-recommended-for-you__cta,.c-googleadslot' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lehuffpost.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.gdpr-glm-standard,.leaderboard-flex-placeholder-desktop,.leaderboard-flex-placeholder,.gdpr-hfp-wall' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/qctimes.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#x-reveal-ad' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | 6 | document.querySelector('#site-container').style.marginTop = 0; 7 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thedbk.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#div-gpt-ad-1598471439011-0,#div-gpt-ad-1596391450199-0,#panel-375410-0-1-0,#panel-375410-0-1-1' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/1x1-transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/1x1-transparent.gif -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/2x2-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/2x2-transparent.png -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/3x2-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/3x2-transparent.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/baltimoremag.js: -------------------------------------------------------------------------------- 1 | document 2 | .querySelectorAll(".leadinModal-overlay,.leadinModal-content,#hs-interactives-modal-overlay,#hs-web-interactives-top-anchor") 3 | .forEach((el) => el.remove()); 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/wcfcourier.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#x-reveal-ad' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | 6 | document.querySelectorAll('#site-container').forEach((e) => e.remove()); 7 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/32x32-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palewire/news-homepages/HEAD/newshomepages/extensions/adguard/web-accessible-resources/redirects/32x32-transparent.png -------------------------------------------------------------------------------- /newshomepages/sources/javascript/ledevoir.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#alertesNavigateur,.modal-backdrop,.modal' 3 | ).forEach(el => el.remove()) 4 | 5 | document.getElementsByTagName('body')[0].classList.remove("modal-open") 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/elcorreo_com.js: -------------------------------------------------------------------------------- 1 | const firstDiv = document.querySelector('div'); 2 | if (firstDiv.innerHTML === '') { 3 | firstDiv.remove(); 4 | } 5 | 6 | document.querySelectorAll( 7 | '.v-adv' 8 | ).forEach(el => el.remove()) 9 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/vcstar.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#gnt_mol_oy' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | document.querySelectorAll('[aria-label="advertisement"]').forEach(el => el.style.display = 'none') 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/nytimes.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('[data-testid="StandardAd"],#complianceOverlay').forEach(el => el.remove()) 2 | 3 | document.querySelectorAll( 4 | '.ad,.e1xxpj0j0' 5 | ).forEach(el => el.style.display = "none") 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/france24.js: -------------------------------------------------------------------------------- 1 | document.getElementsByTagName('body')[0].classList.remove("didomi-popup-open") 2 | 3 | document.querySelectorAll( 4 | '.m-block-ad,.o-pwa-ah2s,.o-ad-container,#didomi-host' 5 | ).forEach(el => el.remove()) 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/frednewspost.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#tncms-block-779600,#tncms-block-779599,#ad-779608,#carousel-617349,#ad-985838,#tncms-region-global-container-top,#ad-779608' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lajornada.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.ad,.ad-content' // <-- Pull your page’s identifiers here. If there's more than one thing to target you can comma seperate them. 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lemonde_en.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#banniere_haute,#pave_haut,.gdpr-lmd-wall,.gdpr-lmd-standard,.gdpr-lmd-standard gdpr-lmd-standard--transparent-deny,.gdpr-lmd-wall__content' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/lemondefr.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#banniere_haute,#pave_haut,.gdpr-lmd-wall,.gdpr-lmd-standard,.gdpr-lmd-standard gdpr-lmd-standard--transparent-denys,.gdpr-lmd-wall__content' 3 | ).forEach(el => el.remove()) 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/france24_en.js: -------------------------------------------------------------------------------- 1 | document.getElementsByTagName('body')[0].classList.remove("didomi-popup-open") 2 | 3 | document.querySelectorAll( 4 | '.m-block-ad,.o-pwa-ah2s,.o-ad-container,#didomi-host' 5 | ).forEach(el => el.remove()) 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/baltimoresun.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | 'div#fPennS1oKJvw1t,.met-footer-toast,.met-toaster-zepcomponent,#ad_unit,.dfp-feature wrapper,.bxc,#onesignal-slidedown-container,.dfp-feature' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/telegraph.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.martech-modal-component-overlay' 3 | ).forEach(el => el.remove()) 4 | 5 | document.querySelectorAll( 6 | '.subscribe-banner,#advert_tmg_ban' 7 | ).forEach(el => el.style.display = 'none') 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thephilacitizen.js: -------------------------------------------------------------------------------- 1 | // Loop through all elements in the document 2 | // and remove any with a z-index over 9999 3 | document.querySelectorAll('*').forEach(el => { 4 | if (window.getComputedStyle(el).zIndex > 9999) { 5 | el.remove() 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you've found a vulnerability in the project, please contact maintainer Ben Welsh at [b@palewi.re](mailto:b@palewi.re) or send him a direct message on Twitter at [@palewire](https://www.twitter.com/palewire). 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/abc_es.js: -------------------------------------------------------------------------------- 1 | const firstDiv = document.querySelector('div'); 2 | if (firstDiv.innerHTML === '') { 3 | firstDiv.remove(); 4 | } 5 | 6 | document.querySelectorAll( 7 | '.v-adv,.voc-container-fw,.voc-container--bg-color' 8 | ).forEach(el => el.remove()) 9 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/guardian.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.ad_unit,div[id^="sp_message_container"],#sp_message_container_597005,.top-banner-ad-container,.site-message--banner,.remote-banner,.message-container,#notice' 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/libe.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '[class^="StickyAd"]' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | document.querySelectorAll( 6 | '.message-container,.message-container,[id^="sp_message_container"]' 7 | ).forEach(el => el.remove()) 8 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/cnn.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('#scrollover-ad-wrap').forEach((e) => e.remove()); 2 | 3 | document.querySelectorAll( 4 | '.ad,.ad--epic' 5 | ).forEach(el => el.remove()) 6 | 7 | document.querySelectorAll('body').forEach((e) => { 8 | e.style.paddingTop = 0; 9 | }) 10 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/corriere.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#ac-Overlay,#ac-notice,.privacy-cp-wall,.bck-adblock,.tp-modal' 3 | ).forEach(el => el.remove()) 4 | 5 | // Remove the has--adblock class from the html tag element 6 | document.querySelector('html').classList.remove('has--adblock') 7 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/pajaropolitico.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#div-gpt-ad-8828989-1,#div-gpt-ad-8828989-2,#div-gpt-ad-8828989-3,.app_fondea' // <-- Pull your page’s identifiers here. If there's more than one thing to target you can comma seperate them. 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | A guide on how to contribute to the repository can be found at [https://homepages.news](https://palewi.re/docs/news-homepages/#contributing). It includes [how to add a site](https://palewi.re/docs/news-homepages/adding.html) or a [Slack bot](https://palewi.re/docs/news-homepages/slack.html). 4 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/css/nanobar.css: -------------------------------------------------------------------------------- 1 | .adg-progress-bar { 2 | height: 2px; 3 | } 4 | 5 | .adg-progress-bar .bar { 6 | background: #66b574; 7 | background: linear-gradient(45deg, rgba(99, 125, 120, 1) 0%, rgba(88, 177, 101, 1) 100%); 8 | box-shadow: 0 0 10px #66b574, 0 0 5px #66b574; 9 | } 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md LICENSE CONTRIBUTING.md CODE_OF_CONDUCT.md SECURITY.md 2 | recursive-include newshomepages/extensions * 3 | recursive-include newshomepages/sources * 4 | recursive-exclude extracts * 5 | recursive-exclude _notebooks * 6 | recursive-exclude _site * 7 | recursive-exclude _dist * 8 | recursive-exclude tests * 9 | -------------------------------------------------------------------------------- /_site/_templates/bundles.md.tmpl: -------------------------------------------------------------------------------- 1 | # Categories 2 | 3 | Sites are grouped into {{ bundle_list|length }} bundles based on location, ideology and topic. 4 | 5 | | Bundle | 6 | | :----- | 7 | {% for obj in bundle_list -%} 8 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/bundles/{{ obj.slug }}.html)| 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/pioneerpress.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.Mg2-connext,.mg2bn-body,.pushly_popover-box,.div-gpt-ad-interstitial,[id^="google_ads_"],.pushly_popover,#pushnav-container,#zeus_interstitial,.dfp-ad' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | document.querySelector('#page').style.paddingTop = 0; 6 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/statnews.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .stat-modal-base { 3 | display: none; 4 | `; 5 | var styleSheet = document.createElement("style") 6 | styleSheet.innerText = styles 7 | document.head.appendChild(styleSheet) 8 | 9 | document.querySelectorAll( 10 | '.stat-modal-base' 11 | ).forEach(el => el.remove()) 12 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thetriibe.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.triibe-modal,.pum-overlay' 3 | ).forEach(el => el.remove()) 4 | 5 | const text = 'ad blocker'; 6 | 7 | for (const div of document.querySelectorAll('div')) { 8 | if (div.textContent.toLowerCase().includes(text)) { 9 | div.remove(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thewrap.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | #attentive_overlay { 3 | display: none; 4 | `; 5 | var styleSheet = document.createElement("style") 6 | styleSheet.innerText = styles 7 | document.head.appendChild(styleSheet) 8 | 9 | document.querySelectorAll( 10 | '#attentive_overlay' 11 | ).forEach(el => el.remove()) 12 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/usatoday.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#gnt_mol_oy' 3 | ).forEach(el => el.style.display = 'none') 4 | 5 | document.querySelectorAll('[aria-label="advertisement"]').forEach(el => el.style.display = 'none') 6 | document.querySelectorAll('#onetrust-banner-sdk').forEach(el => el.style.display = 'none') 7 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/estadao.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | ".evg-overlay,.evg-popup,#lgpd,#house-ad-link,.house-ad-link" 3 | ).forEach((e) => e.remove()); 4 | 5 | // Remove any element with a class that starts with styles__Container-sc- 6 | document.querySelectorAll("[class^='styles__Container-sc-']").forEach((e) => e.remove()); 7 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/el_universal_mx.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#sliding-popup,.popup-content,#canvas,#dfp-ad-portada_desk_skin1-wrapper,#dfp-ad-portada_desk_bottom1-wrapper' // <-- Pull your page’s identifiers here. If there's more than one thing to target you can comma seperate them. 3 | ).forEach(el => el.style.display = 'none') 4 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/moreperfectus.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .glow, .glow--active { 3 | display: none; 4 | `; 5 | var styleSheet = document.createElement("style") 6 | styleSheet.innerText = styles 7 | document.head.appendChild(styleSheet) 8 | 9 | document.querySelectorAll( 10 | '.glow,.glow--active' 11 | ).forEach(el => el.remove()) 12 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/ntdaily.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .mc-modal,.mc-modal-bg { 3 | display: none; 4 | `; 5 | var styleSheet = document.createElement("style") 6 | styleSheet.innerText = styles 7 | document.head.appendChild(styleSheet) 8 | 9 | document.querySelectorAll( 10 | '.mc-modal,.mc-modal-bg' 11 | ).forEach(el => el.remove()) 12 | -------------------------------------------------------------------------------- /tests/test_extract.py: -------------------------------------------------------------------------------- 1 | from click.testing import CliRunner 2 | 3 | from newshomepages.extract import cli 4 | 5 | 6 | def test_extract_item(tmp_path): 7 | """Test a site's item download.""" 8 | runner = CliRunner() 9 | result = runner.invoke(cli, ["items", "latimes", "--output-dir", tmp_path]) 10 | assert result.exit_code == 0 11 | -------------------------------------------------------------------------------- /tests/test_adstxt.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from click.testing import CliRunner 3 | 4 | from newshomepages import adstxt 5 | 6 | 7 | @pytest.mark.vcr() 8 | def test_adstxt_cli(tmp_path): 9 | """Test a single ads.txt request.""" 10 | runner = CliRunner() 11 | result = runner.invoke(adstxt.cli, ["reuters", "-o", tmp_path]) 12 | assert result.exit_code == 0 13 | -------------------------------------------------------------------------------- /_site/_templates/languages.md.tmpl: -------------------------------------------------------------------------------- 1 | # Languages 2 | 3 | The archiving routine is currently saving sites in {{ language_list|length }} languages. 4 | 5 | | Country | Sites | 6 | | :------- | :---- | 7 | {% for obj in language_list -%} 8 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/languages/{{ obj.part1|lower }}.html)|{{ obj.site_list|length }}| 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/bn/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/kn/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/lv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/sr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/ta/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/te/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/latimes.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '[data-ad-rendered],#ensNotifyBanner' 3 | ).forEach(el => el.style.display = 'none'); 4 | 5 | setTimeout(function(){ 6 | scroll(0, 100); 7 | setTimeout( function(){ 8 | document.querySelectorAll('modality-custom-element').forEach(el => el.remove()); 9 | }, 1000) 10 | } , 1000) 11 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/fil/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/_locales/mk-MK/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "AdGuard AdBlocker" 4 | }, 5 | "short_name": { 6 | "message": "AdGuard" 7 | }, 8 | "description": { 9 | "message": "Unmatched adblock extension against advertising and pop-ups. Blocks ads on Facebook, Youtube and all other websites." 10 | } 11 | } -------------------------------------------------------------------------------- /newshomepages/sources/javascript/chicagotribune.js: -------------------------------------------------------------------------------- 1 | window.onload = function() { 2 | var sels = ['.met-footer-toast', '#onesignal-slidedown-container'] 3 | sels.forEach( function(d){ 4 | var el = document.querySelector(d) 5 | if (el != undefined) { 6 | el.remove() 7 | } 8 | }) 9 | } 10 | location.reload() 11 | -------------------------------------------------------------------------------- /tests/test_hyperlinks.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from click.testing import CliRunner 3 | 4 | from newshomepages import hyperlinks 5 | 6 | 7 | @pytest.mark.vcr() 8 | def test_single(tmp_path): 9 | """Test a single hyperlinks download.""" 10 | runner = CliRunner() 11 | result = runner.invoke(hyperlinks.cli, ["latimes", "-o", tmp_path]) 12 | assert result.exit_code == 0 13 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/msnbc.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .notification-soft-optin { 3 | display: none; 4 | `; 5 | 6 | var styleSheet = document.createElement("style") 7 | styleSheet.innerText = styles 8 | document.head.appendChild(styleSheet) 9 | 10 | document.querySelectorAll( 11 | '.header-and-footer--banner-ad,.notification-soft-optin' 12 | ).forEach(el => el.style.display = 'none') 13 | -------------------------------------------------------------------------------- /tests/test_accessibility.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from click.testing import CliRunner 3 | 4 | from newshomepages import accessibility 5 | 6 | 7 | @pytest.mark.vcr() 8 | def test_accessibility_cli(tmp_path): 9 | """Test a single accessibility run.""" 10 | runner = CliRunner() 11 | result = runner.invoke(accessibility.cli, ["drudge", "-o", tmp_path]) 12 | assert result.exit_code == 0 13 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/pulitzercenter.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .sumo-form-wrapper, .sumo-form-wrapper.listbuilder-popup { 3 | display: none; 4 | `; 5 | 6 | var styleSheet = document.createElement("style") 7 | styleSheet.innerText = styles 8 | document.head.appendChild(styleSheet) 9 | 10 | document.querySelectorAll( 11 | '.sumo-form-wrapper,.listbuilder-popup' 12 | ).forEach(el => el.remove()) 13 | -------------------------------------------------------------------------------- /newshomepages/analyze/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from .drudge import cli as cli_drudge 4 | from .lighthouse import cli as cli_lighthouse 5 | from .us_right_wing import cli as cli_us_right_wing 6 | 7 | cli_group = click.CommandCollection( 8 | sources=[ 9 | cli_drudge, 10 | cli_lighthouse, 11 | cli_us_right_wing, 12 | ] 13 | ) 14 | 15 | if __name__ == "__main__": 16 | cli_group() 17 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/shield.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/devtools.html: -------------------------------------------------------------------------------- 1 | AdGuard devtools -------------------------------------------------------------------------------- /_site/_templates/sources.md.tmpl: -------------------------------------------------------------------------------- 1 | # Sites 2 | 3 | The archiving routine is currently capturing {{ site_list|length }} sites. 4 | 5 | | Site | Latest screenshot | 6 | | :---- | :---------------: | 7 | {% for obj in site_list -%} 8 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/sites/{{ obj.handle }}.html)|[🔗](https://raw.githubusercontent.com/palewire/news-homepages/main/latest-screenshots/{{ obj.handle }}.jpg)| 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/popup.html: -------------------------------------------------------------------------------- 1 | AdGuard Popup
-------------------------------------------------------------------------------- /newshomepages/sources/javascript/civilbeat.js: -------------------------------------------------------------------------------- 1 | var styles = ` 2 | .poston-campaign, #om-h8bkg1lyokdvbncwkfgg, .Campaign, .CampaignType--popup, .Campaign--css { 3 | display: none !important; 4 | } 5 | `; 6 | var styleSheet = document.createElement("style") 7 | styleSheet.innerText = styles 8 | document.head.appendChild(styleSheet) 9 | 10 | document.querySelectorAll( 11 | '.poston-campaign,#om-h8bkg1lyokdvbncwkfgg,.Campaign,.CampaignType--popup,.Campaign--css' 12 | ).forEach(el => el.remove()) 13 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thehilltimes.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '.slide-down,.ads' 3 | ).forEach(el => el.remove()) 4 | 5 | var styleSheet = document.createElement('style'); 6 | styleSheet.innerText = '.content-section { padding-top: 0 !important; }'; 7 | document.head.appendChild(styleSheet); 8 | 9 | document.querySelectorAll( 10 | '.content-section' 11 | ).forEach(el => { 12 | el.style.removeProperty('paddingTop'); 13 | el.style.paddingTop = "0px"; 14 | }) 15 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/ar/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/bg/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/et/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/fa/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/hy/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/sv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "" 4 | }, 5 | "userscriptDescription": { 6 | "message": "" 7 | }, 8 | "browserExtensionName": { 9 | "message": "" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-a-broken-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a broken site 3 | about: Is a screenshot busted? Are there popups or ads in the way? Let's get it fixed. 4 | title: "[Fix site]: " 5 | labels: bug, good first issue, help wanted 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Screenshot 11 | [Upload screenshot here] 12 | 13 | A solution is often found by adding JavaScript or CSS via a site-specific include, [as covered in our documentation](https://palewi.re/docs/news-homepages/adding.html#hide-ads-and-popups). 14 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/ad-blocked.html: -------------------------------------------------------------------------------- 1 | Loading
-------------------------------------------------------------------------------- /newshomepages/site/source_list.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create source list.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def source_list(): 16 | """Create source list.""" 17 | site_list = sorted(utils.get_site_list(), key=lambda x: x["name"].lower()) 18 | print(":newspaper: Creating site list page") 19 | _write_template("sources.md", dict(site_list=site_list)) 20 | -------------------------------------------------------------------------------- /newshomepages/site/bundle_list.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create bundle list.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def bundle_list(): 16 | """Create bundle list.""" 17 | bundle_list = sorted(utils.get_bundle_list(), key=lambda x: x["name"].lower()) 18 | print(":basket: Creating bundle list page") 19 | _write_template("bundles.md", dict(bundle_list=bundle_list)) 20 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/filtering-log.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/safebrowsing.html: -------------------------------------------------------------------------------- 1 | Loading
-------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #eee; 3 | background: rgb(41, 42, 45); 4 | font-family: sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 10px 20px; 9 | width: 232px; 10 | } 11 | 12 | a { 13 | color: rgb(220, 149, 255); 14 | text-decoration: underline; 15 | } 16 | 17 | a:hover { 18 | color: rgb(124, 66, 178); 19 | } 20 | 21 | h2 { 22 | margin-top: 1px; 23 | } 24 | 25 | p { 26 | font-size: 13px; 27 | line-height: 17px; 28 | } 29 | 30 | .logo { 31 | width: 170px; 32 | } 33 | -------------------------------------------------------------------------------- /_site/_templates/drudge-top-words.svg.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% for value in obj.timeseries %} 4 | 11 | 12 | {%- endfor %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/thetimes.js: -------------------------------------------------------------------------------- 1 | const style = document.createElement('style') 2 | style.innerHTML = ` 3 | #sp_message_container_523772 { 4 | display:none!important 5 | } 6 | 7 | #ad-news { 8 | display:none!important 9 | } 10 | 11 | react-edition-personalised-article-rail { 12 | display: none!important 13 | }` 14 | 15 | document.head.appendChild(style) 16 | 17 | document.querySelectorAll( 18 | '.type-modal,#ad-header,.Tooltip,#sticky-ad-header,#react-edition-subscription-marketing-banner-slice' 19 | ).forEach(el => el.style.display = 'none') 20 | -------------------------------------------------------------------------------- /tests/test_screenshot.py: -------------------------------------------------------------------------------- 1 | from click.testing import CliRunner 2 | 3 | from newshomepages import screenshot 4 | 5 | 6 | def test_cropped(tmp_path): 7 | """Test a cropped screenshot.""" 8 | runner = CliRunner() 9 | result = runner.invoke(screenshot.cli, ["drudge", "-o", tmp_path]) 10 | assert result.exit_code == 0 11 | 12 | 13 | def test_full(tmp_path): 14 | """Test a full-page screenshot.""" 15 | runner = CliRunner() 16 | result = runner.invoke(screenshot.cli, ["drudge", "-o", tmp_path, "--full-page"]) 17 | assert result.exit_code == 0 18 | -------------------------------------------------------------------------------- /_site/_templates/latest.md.tmpl: -------------------------------------------------------------------------------- 1 | # Latest screenshots 2 | 3 | The most recent homepages captured from {{ site_list|length }} news sites. 4 | 5 |
6 | {% for obj in site_list %} 7 |
8 | 9 | {{ obj.name }} 12 | 13 |

{{ obj.name }}

14 |
15 | {% endfor %} 16 |
17 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/css/fonts.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @font-face { 3 | font-family: 'Open Sans'; 4 | url('../fonts/opensans-regular-webfont.woff2') format('woff2'), 5 | font-weight: normal; 6 | font-style: normal; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Open Sans'; 11 | url('../fonts/opensans-semibold-webfont.woff2') format('woff2'), 12 | font-weight: 600; 13 | font-style: normal; 14 | } 15 | 16 | @font-face { 17 | font-family: 'Open Sans'; 18 | url('../fonts/opensans-bold-webfont.woff2') format('woff2'), 19 | font-weight: bold; 20 | font-style: normal; 21 | } 22 | -------------------------------------------------------------------------------- /newshomepages/site/latest_screenshots.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create a page showing all of the latest screenshots.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def latest_screenshots(): 16 | """Create page showing all of the latest screenshots.""" 17 | site_list = sorted(utils.get_site_list(), key=lambda x: x["name"].lower()) 18 | print(f":camera: Creating latest screenshots page with {len(site_list)} sites") 19 | _write_template("latest.md", dict(site_list=site_list)) 20 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Options 5 | 6 | 7 | 8 | 9 |
10 | 11 |

12 | 13 |

14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /newshomepages/site/language_list.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create language list.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def language_list(): 16 | """Create language list.""" 17 | language_list = utils.get_language_list() 18 | for d in language_list: 19 | d["site_list"] = utils.get_sites_in_language(d["part1"]) 20 | print("🗣️ Creating language list page") 21 | _write_template( 22 | "languages.md", 23 | dict(language_list=language_list), 24 | ) 25 | -------------------------------------------------------------------------------- /_site/_templates/countries.md.tmpl: -------------------------------------------------------------------------------- 1 | # Countries 2 | 3 | The archiving routine is currently saving sites in {{ has_list|length }} countries. 4 | 5 | | Country | Sites | 6 | | :------- | :---- | 7 | {% for obj in has_list -%} 8 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/countries/{{ obj.alpha2|lower }}.html)| 9 | {% endfor %} 10 | 11 | The system is not yet archiving sites in the following {{ hasnot_list|length }} countries. Help us expand our coverage. Read [our guide to adding sites](https://palewi.re/docs/news-homepages/adding.html) and make a contribution. 12 | 13 | {% for obj in hasnot_list -%} 14 | * {{ obj.name }} 15 | {% endfor %} 16 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/options.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/content-script.js: -------------------------------------------------------------------------------- 1 | 'use strict';(() => { 2 | const scriptTag = document.createElement('script'); 3 | scriptTag.setAttribute('type', 'text/javascript'); 4 | scriptTag.className = 'extra'; 5 | const browser = window.browser || chrome; 6 | const scriptSource = browser.runtime.getURL('userscript.js'); 7 | const hash = Math.random().toString(36).substring(5); 8 | scriptTag.setAttribute('src', `${scriptSource}?${hash}`); 9 | const parent = document.head || document.documentElement; 10 | parent.appendChild(scriptTag); 11 | if (scriptTag.parentNode) { 12 | scriptTag.parentNode.removeChild(scriptTag); 13 | } 14 | })(); -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/reload-ico-green.svg: -------------------------------------------------------------------------------- 1 | Group 20 -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /newshomepages/site/status_report.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create a status report.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def status_report(): 16 | """Create a status report.""" 17 | print("📊 Creating status report") 18 | 19 | # Get the latest status report extract 20 | url = "https://archive.org/download/news-homepages-extracts/status-report.json" 21 | context = utils.get_json_url(url, verbose=True) 22 | 23 | # Write the template 24 | _write_template("status-report.md", context) 25 | 26 | 27 | if __name__ == "__main__": 28 | cli() 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | An open-source archive that gathers, saves, shares and analyzes news homepages 2 | 3 | ## Links 4 | 5 | - Task runner: [github.com/palewire/news-homepages-runner](https://github.com/palewire/news-homepages-runner) 6 | - Internet Archive: [archive.org/details/news-homepages](https://archive.org/details/news-homepages) 7 | - Mastodon [mastodon.palewi.re/@newshomepages](https://mastodon.palewi.re/@newshomepages) 8 | - Documentation: [palewi.re/docs/news-homepages/](https://palewi.re/docs/news-homepages/index.html) 9 | - Packaging: [pypi.org/project/newshomepages](https://pypi.org/project/newshomepages/) 10 | - How to add a site: [palewi.re/docs/news-homepages/adding.html](https://palewi.re/docs/news-homepages/adding.html) 11 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/zh/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra 旨在解决常规的广告拦截规则无能为力的复杂情况。" 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra 浏览器扩展" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra 是一款应与 AdGuard 或其他全面广告拦截器一起使用的扩展。除了工具栏中的扩展程序图标外,它不提供任何用户界面。" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "点击{link}此处{/link},您可以查看其生效的网站列表。 请注意,对不在此列表中的网站,它没有任何作用。" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "如果您遇到任何问题,请{link}将其报告给我们{/link}。" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extraは、通常の広告ブロックルールが十分ではない複雑なケースを解決するために設計されています。" 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extraブラウザ拡張機能" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}こちら{/link}では、AdGuard Extraが動作するウェブサイトのリストを確認することができます。※このリストに載っていないサイトではAdGuard Extraは何もしません。" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "問題がある場合は、{link}こちらから{/link}ご報告いただくようお願いいたします。" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/zh-TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "當常規的廣告封鎖規則不夠時,AdGuard Extra 旨在解決複雜的情況。" 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra 瀏覽器擴充功能" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra 是一款被假設與像 AdGuard 或任何其它之全面的廣告封鎖器一起被使用之夥伴擴充功能。除在該工具列中的擴充功能圖示之外,它沒提供使用者介面。" 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}於此{/link},您可查看它起作用的網站之清單。請注意,它於非在此清單中的網站上不做任何事情。" 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "如果您遇到有關這個的任何問題,請{link}向我們報告它們{/link}。" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/test_robotstxt.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import pytest 4 | import sqlite_robotstxt 5 | from click.testing import CliRunner 6 | 7 | from newshomepages import robotstxt 8 | 9 | 10 | @pytest.mark.vcr() 11 | def test_robotstxt_cli(tmp_path): 12 | """Test a single robots.txt request.""" 13 | runner = CliRunner() 14 | result = runner.invoke(robotstxt.cli, ["latimes", "-o", tmp_path]) 15 | assert result.exit_code == 0 16 | 17 | 18 | def test_sqlite_extensions(): 19 | """Verify that the sqlite extension can be loaded.""" 20 | db = sqlite3.connect(":memory:") 21 | db.enable_load_extension(True) 22 | sqlite_robotstxt.load(db) 23 | db.enable_load_extension(False) 24 | db.execute("select robotstxt_version()").fetchone() 25 | db.close() 26 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/css/devtools/custom.css: -------------------------------------------------------------------------------- 1 | .styles-section .style-properties .enabled-button { 2 | visibility: visible !important; 3 | } 4 | 5 | #select-attributes-checkbox { 6 | margin-left: 0px; 7 | vertical-align: middle; 8 | } 9 | 10 | .action-button { 11 | margin-top: 4px; 12 | margin-bottom: 4px; 13 | margin-left: 4px; 14 | } 15 | 16 | .rule-input { 17 | padding: 4px; 18 | } 19 | 20 | .webkit-css-property { 21 | color: #5a5a5a; 22 | } 23 | 24 | #filter-rule-text { 25 | border-left: none; 26 | border-right: none; 27 | border-top: none; 28 | border-bottom-width: 1px; 29 | } 30 | 31 | .separator-attributes { 32 | min-height: 21px; 33 | } 34 | 35 | .separator-properties { 36 | min-height: 17px; 37 | } -------------------------------------------------------------------------------- /newshomepages/site/country_list.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | 4 | from .. import utils 5 | from .utils import _write_template 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """Create country list.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def country_list(): 16 | """Create country list.""" 17 | country_list = utils.get_country_list() 18 | for c in country_list: 19 | c["site_list"] = utils.get_sites_in_country(c["alpha2"]) 20 | print("🗺️ Creating country list page") 21 | _write_template( 22 | "countries.md", 23 | dict( 24 | has_list=[c for c in country_list if len(c["site_list"])], 25 | hasnot_list=[c for c in country_list if not len(c["site_list"])], 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /newshomepages/sources/javascript/news_letter.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll( 2 | '#top_banner,#topBanner' 3 | ).forEach(el => el.remove()) 4 | 5 | var styles = ` 6 | .onesignal-slidedown-container { 7 | display: none !important; 8 | visibility: hidden; 9 | position: absolute; 10 | right: -1000000; 11 | } 12 | `; 13 | var styleSheet = document.createElement("style") 14 | styleSheet.innerText = styles 15 | document.head.appendChild(styleSheet) 16 | 17 | function getZ (el) { 18 | const z = parseFloat(window.getComputedStyle(el).zIndex); 19 | if (Number.isNaN(z)) { 20 | return 0; 21 | } else { 22 | return z; 23 | } 24 | } 25 | const eleList = Array.from(document.querySelectorAll('body *')); 26 | eleList.filter(el => getZ(el) > 10000).forEach(el => el.remove()); 27 | -------------------------------------------------------------------------------- /_site/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | 22 | livehtml: 23 | sphinx-autobuild -b html $(SOURCEDIR) $(BUILDDIR)/html 24 | -------------------------------------------------------------------------------- /newshomepages/extract/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from .accessibility import cli as cli_accessibility 4 | from .consolidate import cli as cli_consolidate 5 | from .hyperlinks import cli as cli_hyperlinks 6 | from .items import cli as cli_items 7 | from .lighthouse import cli as cli_lighthouse 8 | from .robotstxt import cli as cli_robotstxt 9 | from .status_report import cli as cli_status_report 10 | from .wayback import cli as cli_wayback 11 | 12 | cli_group = click.CommandCollection( 13 | sources=[ 14 | cli_consolidate, 15 | cli_items, 16 | cli_accessibility, 17 | cli_hyperlinks, 18 | cli_lighthouse, 19 | cli_robotstxt, 20 | cli_status_report, 21 | cli_wayback, 22 | ] 23 | ) 24 | 25 | if __name__ == "__main__": 26 | cli_group() 27 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/filter-download.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Triangle 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra는 일반적인 광고 차단 규칙이 충분하지 않은 복잡한 문제를 해결하도록 설계되었습니다." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra 브라우저 확장 프로그램" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra는 AdGuard 같은 풀스케일 광고 차단기와 사용되어야 하는 컴패니언 확장 프로그램입니다. 툴바에서 확장 프로그램 아이콘에 대한 사용자 인터페이스 저장을 제공하지 않습니다." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}여기{/link}에서 작동하는 웹사이트 목록을 확인할 수 있습니다. 목록에 없는 웹사이트에는 아무 것도 하지 않는다는 것을 알아두세요." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "이것과 관련해서 문제가 있다면 {link}우리에게 알려주세요{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/tick.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/js/preload-theme.js: -------------------------------------------------------------------------------- 1 | /* 2 | this script is injected at the top of the page to display 3 | the desired color theme before the main bundle is loaded 4 | */ 5 | (function () { 6 | const urlSearchParams = new URLSearchParams(window.location.search); 7 | const theme = urlSearchParams.get('theme'); 8 | /* 9 | if theme parameter is missing or a system theme is selected, 10 | the desired color is selected using the css media query 11 | */ 12 | if (!theme || theme === 'system') { 13 | return; 14 | } 15 | // the color changes through the selector so that it could be rewritten by css from the main bundle 16 | if (theme === 'dark') { 17 | document.body.classList.add('body_dark'); 18 | } 19 | if (theme === 'light') { 20 | document.body.classList.add('body_light'); 21 | } 22 | })(); 23 | -------------------------------------------------------------------------------- /newshomepages/site/country_detail.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | from rich.progress import track 4 | 5 | from .. import utils 6 | from .utils import _write_template 7 | 8 | 9 | @click.group() 10 | def cli(): 11 | """Create country detail pages.""" 12 | pass 13 | 14 | 15 | @cli.command() 16 | def country_detail(): 17 | """Create country detail pages.""" 18 | country_list = utils.get_country_list() 19 | print(f"🗺️ Creating {len(country_list)} country detail pages") 20 | 21 | # For each site ... 22 | for obj in track(country_list): 23 | site_list = utils.get_sites_in_country(obj["alpha2"]) 24 | if not len(site_list): 25 | continue 26 | context = { 27 | "country": obj, 28 | "site_list": sorted(site_list, key=lambda x: x["name"].lower()), 29 | } 30 | _write_template( 31 | "country_detail.md", context, f"countries/{obj['alpha2'].lower()}.md" 32 | ) 33 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/he/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra מתוכנן לפתור מקרים מורכבים בהם כללים רגילים של חסימת פרסומות אינם מספיקים." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra הרחבת דפדפן" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra הוא הרחבה מלווה אשר אמורה להיות בשימוש עם חוסם פרסומות בקנה מידה מלא כמו AdGuard או כל אחד אחר. הוא אינו מספק ממשק משתמש עבור איקון ההרחבה בסרגל הכלים." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}כאן{/link} אתה יכול לבדוק את הרשימה של אתרים שבהם הוא עובד. אנא שים לב שהוא אינו עושה שום דבר באתרים אשר אינם נמצאים ברשימה." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "אם אתה נתקל בסוגיות כלשהן, אנא {link}דווח לנו עליהן{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/arrow-down-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Icons/24/search green Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Line 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /newshomepages/site/language_detail.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | from rich.progress import track 4 | 5 | from .. import utils 6 | from .utils import _write_template 7 | 8 | 9 | @click.group() 10 | def cli(): 11 | """Create languages detail pages.""" 12 | pass 13 | 14 | 15 | @cli.command() 16 | def language_detail(): 17 | """Create languages detail pages.""" 18 | language_list = utils.get_language_list() 19 | print(f"🗣️ Creating {len(language_list)} language detail pages") 20 | 21 | # For each site ... 22 | for obj in track(language_list): 23 | site_list = utils.get_sites_in_language(obj["part1"]) 24 | if not len(site_list): 25 | continue 26 | context = { 27 | "language": obj, 28 | "site_list": sorted(site_list, key=lambda x: x["name"].lower()), 29 | } 30 | _write_template( 31 | "language_detail.md", context, f"languages/{obj['part1'].lower()}.md" 32 | ) 33 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/fullscreen-user-rules.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /newshomepages/site/bundle_detail.py: -------------------------------------------------------------------------------- 1 | import click 2 | from rich import print 3 | from rich.progress import track 4 | from slugify import slugify 5 | 6 | from .. import utils 7 | from .utils import _write_template 8 | 9 | 10 | @click.group() 11 | def cli(): 12 | """Create bundle detail pages.""" 13 | pass 14 | 15 | 16 | @cli.command() 17 | def bundle_detail(): 18 | """Create bundle detail pages.""" 19 | # Get all bundles 20 | bundle_list = sorted(utils.get_bundle_list(), key=lambda x: x["name"].lower()) 21 | print(f":basket: Creating {len(bundle_list)} bundle detail pages") 22 | 23 | # For each site ... 24 | for bundle in track(bundle_list): 25 | bundle["hashtag"] = slugify(bundle["name"], separator="") 26 | site_list = utils.get_sites_in_bundle(bundle["slug"]) 27 | context = { 28 | "bundle": bundle, 29 | "site_list": sorted(site_list, key=lambda x: x["name"].lower()), 30 | } 31 | _write_template("bundle_detail.md", context, f"bundles/{bundle['slug']}.md") 32 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/da/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra er designet til at løse komplicerede tilfælde, hvor alm. adblockingregler ikke er nok." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra-browserudvidelse" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra er en ledsagerudvidelse, der forudsætter brug sammen med en fuldblods adblocker såsom AdGuard el.lign. Den har ingen grænseflade gem-mulighed til udvidelsesikonet på værktøjsbjælken." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Hér{/link} kan du tjekke listen over de websteder, hvor den fungerer. Bemærk, at den intet gør på de websteder, som ikke er på denne liste." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Støder du på problemer, så {link}anmeld dem venligst til os{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/cs/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra má za úkol řešit složité případy, když běžná pravidla pro blokování reklam nestačí." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Rozšíření prohlížeče AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra je další rozšíření, které lze použít spolu s plnohodnotnými blokátory reklam, jako je AdGuard nebo jiné. Nemá žádné uživatelské rozhraní kromě ikony rozšíření na panelu nástrojů." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Zde{/link} si můžete prohlédnout seznam webových stránek, na kterých funguje. Upozorňujeme, že na webových stránkách, které nejsou na tomto seznamu, nedělá nic." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Pokud narazíte na nějaké problémy, prosím {link}nahlaste nám je{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra is designed to solve complicated cases when regular ad blocking rules aren't enough." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra browser extension" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra is a companion extension that is supposed to be used with a full-scale ad blocker like AdGuard or any other. It provides no user interface save for the extension icon in the toolbar." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Here{/link} you can check out the list of the websites where it works. Please note that it doesn't do anything on the websites that are not on this list." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "If you encounter any issues with this, please {link}report them to us{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/hr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra je dizajniran za rješavanje kompleksnih slučajeva kada opća pravila blokade oglasa nisu dovoljna." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra proširenje preglednika" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra je popratno proširenje koje se koristi za upotrebu s opsežnim blokerom oglasa poput AdGuarda ili bilo kojeg drugog. Ne nudi spremanje korisničkog sučelja za ikonu proširenja na alatnoj traci." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Ovdje{/link} možete pogledati popis web stranica na kojima to radi. Imajte na umu da ne čini ništa na web stranicama koje nisu na ovom popisu." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Ako naiđete na bilo kakve probleme, {link}prijavite nam ih{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra is bedoeld om ingewikkelde gevallen op te lossen wanneer de standaard advertentie blokkeringsregels niet voldoende blijken." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra browser uitbreiding" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra is een uitbreiding die bedoeld is om te gebruiken naast een complete ad blocker zoals AdGuard of een andere. Het heeft zelf geen interface behalve het pictogram van de uitbreiding in de werkbalk." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Hier{/link} kan je nagaan op welke websites het werkt. Noteer dat het niks doet op websites die niet op de lijst staan." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Bij problemen hiermee, ajb {link}rapporteer ze aan ons{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/ro/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra este creeat să rezolve cazuri complexe când regulile uzuale de blocat reclame sunt insuficiente." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Extensie browser AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra este o extensie companion folosită cu un blocant de reclame complet ca AdGuard sau oricare altul. Nu oferă o interfață utilizator ci doar pictograma extensiei în bara de instrumente." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Aici{/link} puteți consulta lista de site-uri web unde funcționează. Vă rugăm să rețineți că nu face nimic pe site-uri web care nu sunt pe această listă." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Orice problemă întâlnită cu aceasta, vă rugăm {link}să ne-o raportați{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/sk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra má za úlohu riešiť zložité prípady, keď bežné pravidlá blokovania reklám nestačia." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra rozšírenie prehliadača" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra je sprievodné rozšírenie, ktoré sa má používať s blokovaním reklám v plnom rozsahu, ako je AdGuard alebo iné. Neposkytuje žiadne užívateľské rozhranie na uloženie ikony rozšírenia na paneli nástrojov." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Tu{/link} si môžete pozrieť zoznam webových stránok, na ktorých funguje. Upozorňujeme, že na webových stránkach, ktoré nie sú na tomto zozname, nerobí nič." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Ak narazíte na akékoľvek problémy, {link}nahláste nám ich{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/fi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra on suunniteltu ratkaisemaan monimutkaisia tapauksia kun tavalliset mainosten estosäännöt eivät riitä." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra -laajennus" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra on kumppanilaajennus, jota on tarkoitus käyttää laajamittaisen mainoseston kuten AdGuardin tai vastaavan kanssa. Se ei sisällä työkalupalkin kuvakketta lukuunottamatta lainkaan käyttöliittymää." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Täällä{/link} voit tarkastella listaa verkkosivustoista, joilla laajennus toimii. Huomioithan, että toiminto vaikuttaa vain sivustoihin, jotka ovat tällä listalla." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Jos havaitset ongelmia, pyydämme, että {link}raportoisit ne meille{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/no/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra er utformet for å løse kompliserte saker der vanlige blokkeringsoppføringer ikke strekker til." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra-nettleserutvidelse" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra er en følgesutvidelse som er ment å brukes sammen med en fullskala reklameblokkerer som AdGuard eller en annen en. Den har ikke noe brukergrensesnitt, bortsett fra utvidelsesikonet i verktøylinjen." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Her{/link} kan du sjekke ut listen over nettsteder som det fungerer på. Bemerk at den ikke gjør noe på nettsteder som ikke er på denne listen." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Hvis du støter på noen problemer med dette, vennligst {link}meld dem inn til oss{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/sr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard ekstra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra bi trebalo da reši komplikovane slučajeve u kojima standardna pravila blokiranja reklama nisu dovoljna." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard ekstra proširenje za preglednike" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra je saradničko proširenje koje je predviđeno da se koristi sa potpunim blokatorom reklama kao što je AdGuard ili neki drugi. On nema korisničko okruženje osim ikonice proširenja na alatnoj traci." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Ovde{/link} možete proveriti listu sajtova gde to radi. Imajte na umu da se ništa neće dogoditi sa sajtovima koji nisu na listi." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Ako naiđete na bilo koji problem sa ovim, molimo vas da nam to {link}prijavite{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/lt/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra yra skirtas išspręsti sudėtingesnius atvejus, kai nepakanka įprastų skelbimų blokavimo taisyklių." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra naršyklės plėtinys" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra yra papildomas plėtinys, kuris turėtų būti naudojamas su visaverčiu reklamos blokatoriumi, pvz., AdGuard ar bet kuriuo kitu. Jis neturi vartotojo sąsajos, išskyrus įrankių juostoje esančią plėtinio piktogramą." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Čia{/link} galite peržiūrėti svetainių, kuriose jis veikia, sąrašą. Atkreipkite dėmesį, kad jis nieko nedaro svetainėse, kurių nėra šiame sąraše." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Jei kyla kokių nors problemų dėl to, {link}praneškite mums apie jas{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra призначений для вирішення складних завдань, коли звичайних правил для блокування реклами недостатньо." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Розширення для браузера AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra — це додаткове розширення, призначене для використання з повноцінними блокувальниками вмісту, наприклад, AdGuard чи будь-якими іншими. Воно не має користувацького інтерфейсу, а лише піктограму в панелі завдань." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "Розширення працює не всюди, а лише на{link}вебсайтах зі списку{/link}. Зауважте, що в разі відсутності сайту в списку, розширення не працюватиме там." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Якщо у вас виникли проблеми, {link}повідомте про них{/link}, будь ласка." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/test_archive.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from newshomepages import archive, utils 5 | 6 | 7 | def test_archive_clean_handle(): 8 | """Test archive handle cleaning.""" 9 | 10 | def _get_handle(x): 11 | return utils.safe_ia_handle(utils.get_site(x)["handle"]) 12 | 13 | assert _get_handle("latimes") == "latimes" 14 | assert _get_handle("CNN") == "cnn" 15 | 16 | 17 | def test_archive_metadata(): 18 | """Test the method that creates a metadata dict for uploading to archive.org.""" 19 | data = utils.get_site("latimes") 20 | metadata = archive._get_item_metadata(data) 21 | assert metadata["collection"] == os.getenv("IA_COLLECTION") 22 | assert metadata["title"].startswith(f"{data['name']} homepages in ") 23 | 24 | 25 | def test_archive_file_dict(): 26 | """Test the method that creates a file dict that's uploaded to archive.org.""" 27 | data = utils.get_site("latimes") 28 | this_dir = Path(__file__).parent.absolute() 29 | input_dir = this_dir / "files" 30 | d = archive._get_file_dict(data, input_dir) 31 | assert len(d.keys()) == 1 32 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/id/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra dirancang untuk menyelesaikan kasus rumit saat aturan pemblokiran iklan biasa tidak cukup." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Ekstensi Peramban AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra adalah ekstensi pendamping yang seharusnya digunakan dengan pemblokir iklan skala penuh seperti AdGuard atau lainnya. Ini tidak menyediakan antarmuka pengguna kecuali ikon ekstensi di bilah alat." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Di sini{/link} anda bisa mengecek daftar situs web di mana bisa berfungsi. Perlu diketahui bahwa itu tidak akan melakukan apapun kepada situs web yang tidak tercantum di dalam daftar." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Jika Anda mengalami masalah dengan ini, harap {link}laporkan ke kami{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/sl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "Uporabi AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra naj bi rešil zapletene primere, ko običajna pravila za zaviranje oglasov niso dovolj." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Dodatna razširitev brskalnika" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra je spremljevalna razširitev, ki naj bi se uporabljala s splošnim zaviralcem oglasov, kot je AdGuard ali kateri koli drug. Ne omogoča nobenega shranjevanja uporabniškega vmesnika z ikono razširitve v orodni vrstici." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Tukaj{/link} si lahko ogledate seznam spletnih strani na katerih deluje. Upoštevajte, da ne deluje na spletnih straneh, ki niso na tem seznamu." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Če naletite na kakršne koli težave s tem, prosimo, da nam to {link} prijavite{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/pt/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "O AdGuard Extra é indicado para resolver casos complicados onde as regras regulares de bloqueio de anúncios não são suficientes." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Extensão para navegador do AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra é uma extensão complementar que deve ser usada com um bloqueador de anúncios em escala real como o AdGuard ou qualquer outro. Ele não fornece nenhuma interface de usuário exceto o ícone da extensão na barra de ferramentas." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Aqui{/link} você pode conferir a lista dos sites onde ele funciona. Observe que ele não faz nada nos sites que não estão nesta lista." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Se você encontrar algum problema. Por favor, {link}informe-nos{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/be/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra прызначаны для вырашэння складаных выпадкаў, калі звычайных правілаў блакавання рэкламы недастаткова." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Браўзарнае пашырэнне AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra - гэта спадарожнае пашырэнне, якое мяркуецца выкарыстоўваць з поўнапамерным блакавальнікам рэкламы, такім як AdGuard ці любым іншым. Ён не падае карыстацкага інтэрфейсу, апроч значка пашырэння на панэлі прылад." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link} Тут {/ link} вы можаце азнаёміцца са спісам сайтаў, дзе ён працуе. Звярніце ўвагу, што ён нічога не робіць на сайтах, якіх няма ў гэтым спісе." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Калі ў вас паўстануць якія-небудзь праблемы, калі ласка, {link} паведаміце пра іх нам {/ link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/pt-PT/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "O AdGuard Extra é indicado para resolver casos complicados onde as regras regulares de bloqueio de anúncios não são suficientes." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Extensão do AdGuard Extra para navegador" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra é uma extensão complementar que deve ser usada com um bloqueador de anúncios em escala real como o AdGuard ou qualquer outro. Ele não fornece nenhuma interface de utilizador exceto o ícone da extensão na barra de ferramentas." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Aqui{/link} tu podes conferir a lista dos sítios onde ele funciona. Observe que ele não faz nada nos sítios que não estão nesta lista." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Se você encontrar algum problema. Por favor, {link}informe-nos{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra wurde entwickelt, um komplexe Fälle zu lösen, wenn normalen Regeln zum Sperren von Werbung nicht ausreichen." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra-Browsererweiterung" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra ist eine Zusatzerweiterung, die mit vollwertigem Werbeblocker wie AdGuard oder anderen verwendet werden kann. Sie bietet keine Benutzeroberfläche außer dem Symbol für die Erweiterung in der Symbolleiste." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Hier{/link} können Sie die Liste der Websites einsehen, auf denen es funktioniert. Bitte beachten Sie, dass nur Websites gefiltert werden, die in der Liste enthalten sind." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Wenn Sie dabei auf Probleme stoßen, {link}melden Sie uns diese bitte{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra está diseñado para resolver casos complicados cuando las reglas regulares para bloqueo de anuncios no son suficientes." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Extensión de navegador AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra es una extensión complementaria que se utiliza con un bloqueador de anuncios a gran escala como AdGuard o cualquier otro. No proporciona ninguna interfaz de usuario, salvo el icono de la extensión en la barra de herramientas." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Aquí{/link} puedes consultar la lista de los sitios web donde funciona. Ten en cuenta que no hace nada en los sitios web que no están en esta lista." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Si encuentras algún problema, por favor {link}infórmanos{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra è progettato per risolvere casi complicati in cui le normali regole di blocca-annunci non sono sufficienti." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Estensione browser AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra è un'estensione complementare che dovrebbe essere utilizzata con un blocco degli annunci su vasta scala come AdGuard o qualsiasi altro. Non fornisce alcuna interfaccia utente tranne l'icona dell'estensione nella barra degli strumenti." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Qui{/link} potrai verificare l'elenco dei siti in cui risulterà funzionante. Si prega di notare che non esegue alcuna operazione sui siti non in elenco." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Se dovessi riscontrare problemi, ti preghiamo di {link}riportaceli{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/pl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra jest przeznaczony do rozwiązywania skomplikowanych przypadków, gdy zwykłe reguły blokowania reklam nie wystarczą." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Rozszerzenie przeglądarki AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra to rozszerzenie towarzyszące, które powinno być używane z pełni funkcjonalnym programem do blokowania reklam, takim jak AdGuard lub jakikolwiek inny. Nie zapewnia żadnego interfejsu użytkownika poza ikoną rozszerzenia na pasku narzędzi." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Tutaj{/link} możesz sprawdzić listę stron internetowych, na których to działa. Należy pamiętać, że nie działa to w witrynach, których nie ma na tej liście." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Jeśli napotkasz jakiekolwiek problemy, {link}zgłoś je nam{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra предназначен для решения более сложных задач, когда обычных правил блокировки рекламы недостаточно." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Браузерное расширение AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra — это сопутствующее расширение, которое предполагается использовать с полноценным блокировщиком рекламы, таким как AdGuard или любым другим. У него нет пользовательского интерфейса, кроме значка расширения на панели инструментов." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Здесь{/link} вы можете ознакомиться со списком сайтов, где расширение работает. Обратите внимание, что оно ничего не делает на сайтах, которых нет в этом списке." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Если у вас возникнут какие-либо проблемы, пожалуйста, {link}сообщите о них нам{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/tr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra, normal reklam engelleme kurallarının yeterli olmadığı karmaşık durumları çözmek için tasarlanmıştır." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra tarayıcı eklentisi" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra, AdGuard veya başka herhangi bir reklam aracı gibi tam ölçekli bir reklam engelleyiciyle kullanılması beklenen tamamlayıcı bir eklentidir. Araç çubuğundaki uzantı simgesi için kullanıcı arabirimi kaydetme olanağı sağlamaz." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Buraya{/link} tıklayarak çalıştığı web sitelerin listesine göz atabilirsiniz. Lütfen, bu listede bulunmayan web sitelerinde hiçbir şey yapmadığını unutmayın." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Bununla ilgili herhangi bir sorunla karşılaşırsanız, lütfen {link}bunları bize bildirin{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /_site/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from datetime import datetime 4 | from pathlib import Path 5 | 6 | THIS_DIR = Path(__file__).parent.absolute() 7 | 8 | sys.path.insert(0, os.path.abspath("..")) 9 | sys.path.insert(0, str(THIS_DIR.parent)) 10 | 11 | extensions = [ 12 | "myst_parser", 13 | "sphinx.ext.autodoc", 14 | "sphinx_click", 15 | ] 16 | templates_path = ["_templates"] 17 | source_suffix = {".rst": "restructuredtext"} 18 | master_doc = "index" 19 | html_extra_path = ["_extra"] 20 | 21 | project = "News Homepages" 22 | year = datetime.now().year 23 | copyright = f"{year} palewire" 24 | 25 | exclude_patterns = ["_build"] 26 | 27 | html_theme = "palewire" 28 | html_sidebars = { 29 | "**": [ 30 | "about.html", 31 | "navigation.html", 32 | ] 33 | } 34 | html_theme_options = { 35 | "canonical_url": "https://palewi.re/docs/news-homepages/", 36 | } 37 | 38 | html_static_path = ["_static"] 39 | html_js_files = [ 40 | "https://cdn.jsdelivr.net/npm/vega@5.22.1", 41 | "https://cdn.jsdelivr.net/npm/vega-lite@5.2.0", 42 | "https://cdn.jsdelivr.net/npm/vega-embed@6.20.8", 43 | ] 44 | 45 | pygments_style = "sphinx" 46 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra est conçu pour résoudre les cas complexes lorsque les règles de blocage des publicités ne suffisent pas." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Extension pour le navigateur AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra est une extension complémentaire qui est censée être utilisée avec un bloqueur de publicité complet comme AdGuard ou autre. Il ne fournit aucune interface utilisateur, sauf pour l’icône d’extension dans la barre d’outils." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Ici{/link} vous pouvez consulter la liste des sites Web sur lesquels elle fonctionne. Veuillez noter qu’elle ne fait rien sur les sites Web qui ne figurent pas sur cette liste." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Si vous rencontrez des problèmes, veuillez {link}nous les signaler{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/hu/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "Az AdGuard Extra olyan bonyolultabb esetek megoldására szolgál, amikor a hagyományos hirdetésblokkolási szabályok nem elegendőek." 7 | }, 8 | "browserExtensionName": { 9 | "message": "AdGuard Extra böngésző kiegészítő" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "Az AdGuard Extra egy kiegészítő, amelyet egy teljesértékű hirdetésblokkolóval együtt kell használni, mint például az AdGuard vagy bármi más. Nem biztosít felhasználói felületet, az eszközsoron található kiterjesztés ikont leszámítva." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Itt{/link} megtekintheti azon webhelyek listáját, amelyeken működik. Felhívjuk figyelmét, hogy nem csinál semmit azokon a webhelyeken, amelyek nem szerepelnek a listán." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Ha bármilyen észrevétele merülne fel ezzel kapcsolatban, kérjük, {link}jelentse nekünk{/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/_locales/vi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "userscriptName": { 3 | "message": "AdGuard Extra" 4 | }, 5 | "userscriptDescription": { 6 | "message": "AdGuard Extra được thiết kế để giải quyết các vấn đề khi mà các quy tắc chặn quảng cáo thông thường chưa đủ." 7 | }, 8 | "browserExtensionName": { 9 | "message": "Tiện ích mở rộng Trình duyệt AdGuard Extra" 10 | }, 11 | "browserExtensionDescription": { 12 | "message": "AdGuard Extra là một tiện ích mở rộng đồng hành được cho là sẽ được sử dụng với trình chặn quảng cáo toàn diện như AdGuard hoặc bất kỳ tiện ích nào khác. Nó không cung cấp lưu giao diện người dùng cho biểu tượng tiện ích mở rộng trên thanh công cụ." 13 | }, 14 | "browserExtensionHostsMessage": { 15 | "message": "{link}Đây{/link} là nơi bạn có thể kiểm tra danh sách những trang web mà Adguard Extra hoạt động. Xin lưu ý rằng phần mềm sẽ không thay đổi bất cứ gì trên những trang web không được liệt kê ở danh sách này." 16 | }, 17 | "browserExtensionReportMessage": { 18 | "message": "Nếu bạn không gặp bất cứ vấn đề nào với nó, xin vui lòng {link}báo cáo nó cho chúng tôi {/link}." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | click = "*" 8 | pytz = "*" 9 | internetarchive = ">=5.0.4" 10 | python-slugify = "*" 11 | playwright = "*" 12 | jinja2 = "*" 13 | shot-scraper = "*" 14 | pillow = "*" 15 | pandas = "*" 16 | rich = "*" 17 | iso3166 = "*" 18 | python-iso639 = "*" 19 | requests = "*" 20 | retry = "*" 21 | storysniffer = "*" 22 | spacy = "*" 23 | spectra = "*" 24 | "mastodon.py" = "*" 25 | sqlite-robotstxt = "*" 26 | "sqlean.py" = "*" 27 | libtorrent = "*" 28 | 29 | [dev-packages] 30 | pre-commit = "*" 31 | sphinx = "*" 32 | sphinx-autobuild = "*" 33 | myst-parser = "*" 34 | pytest = "*" 35 | flake8 = "*" 36 | flake8-docstrings = "*" 37 | flake8-bugbear = "*" 38 | mypy = "*" 39 | types-requests = "*" 40 | types-python-slugify = "*" 41 | types-pytz = "*" 42 | types-pyyaml = "*" 43 | sphinxcontrib-napoleon = "*" 44 | sphinx-click = "*" 45 | sphinx-palewire-theme = "==0.1.2" 46 | types-retry = "*" 47 | storysniffer = "*" 48 | ipywidgets = "*" 49 | setuptools-scm = "*" 50 | twine = "*" 51 | xlwt = "*" 52 | pytest-env = "*" 53 | pytest-cov = "*" 54 | pytest-vcr = "*" 55 | pytest-xdist = "*" 56 | exceptiongroup = "*" 57 | tomli = "*" 58 | jupyterlab = "*" 59 | 60 | [requires] 61 | python_version = "3.10" 62 | -------------------------------------------------------------------------------- /_site/_templates/language_detail.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | orphan: true 3 | --- 4 | 5 | # {{ language.name }} 6 | 7 | The most recent homepages from {{ site_list|length }} news sites in this language. 8 | 9 |
10 | {% for obj in site_list %} 11 |
12 | 13 | {{ obj.name }} 16 | 17 |

{{ obj.name }}

18 |
19 | {% endfor %} 20 |
21 | 22 | ## About this country 23 | 24 | | Attribute | Value | 25 | | :------------- | :---------------------------------------------------------------------------------- | 26 | | ISO code | {{ language.part1 }} | 27 | 28 | ## Site directory 29 | 30 | | Site | Latest screenshot | 31 | | :---- | :---------------: | 32 | {% for obj in site_list -%} 33 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/sites/{{ obj.handle }}.html)|[🔗](https://raw.githubusercontent.com/palewire/news-homepages/main/latest-screenshots/{{ obj.handle }}.jpg)| 34 | {% endfor %} 35 | -------------------------------------------------------------------------------- /newshomepages/extract/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | from pathlib import Path 3 | from urllib.parse import urlparse 4 | 5 | import pandas as pd 6 | from rich import print 7 | 8 | from .. import utils 9 | 10 | 11 | def _get_json_url(url): 12 | # Prepare a cache 13 | cache_dir = Path("~/.cache").expanduser() 14 | cache_dir.mkdir(parents=True, exist_ok=True) 15 | 16 | # Check if the file has been downloaded 17 | output_path = cache_dir / urlparse(url).path.split("/")[-1] 18 | if output_path.exists(): 19 | print(f":book: Reading in cached file {output_path}") 20 | return pd.read_json(output_path) 21 | else: 22 | # Get the URL 23 | data = utils.get_json_url(url, timeout=60, verbose=True) 24 | 25 | # Parse as a dataframe 26 | df = pd.DataFrame(data) 27 | 28 | # Write to cache 29 | df.to_json(output_path, orient="records", indent=2) 30 | print(f":pencil: Writing to cached file {output_path}") 31 | time.sleep(0.25) 32 | 33 | # Add columns 34 | metadata = utils.parse_archive_url(url) 35 | df["site_handle"] = metadata["handle"] 36 | df["item_identifier"] = metadata["identifier"] 37 | df["file_timestamp"] = metadata["timestamp"] 38 | df["file_url"] = url 39 | 40 | # Return dataframe 41 | return df 42 | -------------------------------------------------------------------------------- /_site/_templates/country_detail.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | orphan: true 3 | --- 4 | 5 | # {{ country.name }} 6 | 7 | The most recent homepages from {{ site_list|length }} news sites in this country. 8 | 9 |
10 | {% for obj in site_list %} 11 |
12 | 13 | {{ obj.name }} 16 | 17 |

{{ obj.name }}

18 |
19 | {% endfor %} 20 |
21 | 22 | ## About this country 23 | 24 | | Attribute | Value | 25 | | :------------- | :---------------------------------------------------------------------------------- | 26 | | ISO code | {{ country.alpha2 }} | 27 | | Twitter hashtag | [#{{ country.alpha2 }}](https://twitter.com/search?q=%23{{ country.alpha2 }}%20from%3A%40newshomepages) | 28 | 29 | ## Site directory 30 | 31 | | Site | Latest screenshot | 32 | | :---- | :---------------: | 33 | {% for obj in site_list -%} 34 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/sites/{{ obj.handle }}.html)| 35 | {% endfor %} 36 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/dropbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguardextra/options.js: -------------------------------------------------------------------------------- 1 | 'use strict';const LOCALE_TAG = 'i18n'; 2 | const EXTRA_ISSUES_URL = 'https://github.com/AdguardTeam/AdGuardExtra/issues'; 3 | const EXTRA_HOSTS_URL = 'https://github.com/AdguardTeam/AdGuardExtra#websites-where-adguard-extra-can-be-useful'; 4 | const LINK_MARKER_START = '{link}'; 5 | const LINK_MARKER_END = '{/link}'; 6 | const getLocale = (key) => { 7 | const browser = window.browser || chrome; 8 | const translation = browser.i18n.getMessage(key); 9 | if (!translation) { 10 | return false; 11 | } 12 | return translation; 13 | }; 14 | const translateAll = () => { 15 | const stringsI18n = document.querySelectorAll(`[${LOCALE_TAG}]`); 16 | [...stringsI18n].forEach((str) => { 17 | const attr = str.getAttribute(LOCALE_TAG); 18 | const locale = getLocale(attr); 19 | if (locale) { 20 | str.innerHTML = locale; 21 | } 22 | }); 23 | }; 24 | const addLink = (id, url) => { 25 | const element = document.querySelector(id); 26 | element.innerHTML = element.innerText 27 | .replace(LINK_MARKER_START, ``) 28 | .replace(LINK_MARKER_END, ''); 29 | }; 30 | window.onblur = () => { 31 | window.close(); 32 | }; 33 | translateAll(); 34 | addLink('#hosts-message', EXTRA_HOSTS_URL); 35 | addLink('#report-message', EXTRA_ISSUES_URL); -------------------------------------------------------------------------------- /newshomepages/site/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from .accessibility_ranking import cli as cli_accessibility_ranking 4 | from .bundle_detail import cli as cli_bundle_detail 5 | from .bundle_list import cli as cli_bundle_list 6 | from .country_detail import cli as cli_country_detail 7 | from .country_list import cli as cli_country_list 8 | from .drudge import cli as cli_drudge 9 | from .language_detail import cli as cli_language_detail 10 | from .language_list import cli as cli_language_list 11 | from .latest_screenshots import cli as cli_latest_screenshots 12 | from .openai import cli as cli_openai 13 | from .performance_ranking import cli as cli_performance_ranking 14 | from .site_detail import cli as cli_site_detail 15 | from .source_list import cli as cli_source_list 16 | from .status_report import cli as cli_status_report 17 | 18 | cli_group = click.CommandCollection( 19 | sources=[ 20 | cli_accessibility_ranking, 21 | cli_bundle_detail, 22 | cli_bundle_list, 23 | cli_country_detail, 24 | cli_country_list, 25 | cli_drudge, 26 | cli_language_detail, 27 | cli_language_list, 28 | cli_latest_screenshots, 29 | cli_openai, 30 | cli_performance_ranking, 31 | cli_site_detail, 32 | cli_source_list, 33 | cli_status_report, 34 | ] 35 | ) 36 | 37 | if __name__ == "__main__": 38 | cli_group() 39 | -------------------------------------------------------------------------------- /newshomepages/extract/items.py: -------------------------------------------------------------------------------- 1 | import typing 2 | from datetime import datetime 3 | from pathlib import Path 4 | 5 | import click 6 | import internetarchive 7 | from retry import retry 8 | from rich import print 9 | 10 | from .. import utils 11 | 12 | 13 | @click.group() 14 | def cli(): 15 | """Download items from our archive.org collection as JSON.""" 16 | pass 17 | 18 | 19 | @cli.command() 20 | @click.argument("handle") 21 | @click.option("-y", "--year", "year", default=None) 22 | @click.option("-o", "--output-dir", "output_dir", default="./") 23 | def items( 24 | handle: str, 25 | year: typing.Optional[typing.Any] = None, 26 | output_dir: str = "./", 27 | ): 28 | """Download items from our archive.org collection as JSON.""" 29 | # Sanitize the year 30 | if year: 31 | year = int(year) 32 | else: 33 | year = datetime.now().year 34 | 35 | # Set the path 36 | output_path = Path(output_dir) 37 | 38 | # Pull the site 39 | site = utils.get_site(handle) 40 | _save_item(site, year, output_path) 41 | 42 | 43 | @retry(tries=5, delay=30, backoff=2) 44 | def _save_item(site: typing.Dict, year: int, output_path: Path): 45 | """Save an item as JSON to disk.""" 46 | identifier = f"{site['handle']}-{year}" 47 | print(f"Downloading `{identifier}` from archive.org") 48 | item = internetarchive.get_item(identifier) 49 | utils.write_json(item.item_metadata, output_path / f"{item.identifier}.json") 50 | -------------------------------------------------------------------------------- /newshomepages/batch.py: -------------------------------------------------------------------------------- 1 | import json 2 | import typing 3 | 4 | import click 5 | 6 | from . import utils 7 | 8 | 9 | @click.group() 10 | def cli(): 11 | """Print a batch of sites.""" 12 | pass 13 | 14 | 15 | @cli.command() 16 | @click.argument("batch") 17 | @click.option("-b", "--batches", "batches", default=10) 18 | def sites_by_batch(batch: str, batches: str): 19 | """Print site handles in the provided batch as a JSON list.""" 20 | site_list = utils.get_site_list() 21 | batch_list = list(utils.batch(site_list, int(batches))) 22 | if int(batch) - 1 not in range(int(batches)): 23 | raise ValueError("Batch number not found") 24 | _dump(batch_list[int(batch) - 1]) 25 | 26 | 27 | @cli.command() 28 | @click.argument("bundle") 29 | def sites_by_bundle(bundle: str): 30 | """Print site handles in the provided bundle as a JSON list.""" 31 | site_list = utils.get_sites_in_bundle(bundle) 32 | _dump(site_list) 33 | 34 | 35 | @cli.command() 36 | @click.argument("country") 37 | def sites_by_country(country: str): 38 | """Print site handles in the provided country as a JSON list.""" 39 | site_list = utils.get_sites_in_country(country) 40 | _dump(site_list) 41 | 42 | 43 | def _dump(site_list: typing.List): 44 | """Print out the provided site list as JSON.""" 45 | handle_list = [s["handle"] for s in site_list] 46 | data = json.dumps(handle_list, indent=2) 47 | click.echo(data) 48 | 49 | 50 | if __name__ == "__main__": 51 | cli() 52 | -------------------------------------------------------------------------------- /_site/_templates/bundle_detail.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | orphan: true 3 | --- 4 | 5 | # {{ bundle.name }} 6 | 7 | The most recent homepages from {{ site_list|length }} news sites in this bundle. 8 | 9 |
10 | {% for obj in site_list %} 11 |
12 | 13 | {{ obj.name }} 16 | 17 |

{{ obj.name }}

18 |
19 | {% endfor %} 20 |
21 | 22 | ## About this bundle 23 | 24 | | Attribute | Value | 25 | | :------------- | :---------------------------------------------------------------------------------- | 26 | | Twitter hashtag | [#{{ bundle.hashtag }}](https://twitter.com/search?q=%23{{ bundle.hashtag }}%20from%3A%40newshomepages) | 27 | | Location | {{ bundle.location }} | 28 | | Timezone | {{ bundle.timezone }} | 29 | 30 | ## Site directory 31 | 32 | | Site | Latest screenshot | 33 | | :---- | :---------------: | 34 | {% for obj in site_list -%} 35 | |[{{ obj.name }}](https://palewi.re/docs/news-homepages/sites/{{ obj.handle }}.html)|[🔗](https://raw.githubusercontent.com/palewire/news-homepages/main/latest-screenshots/{{ obj.handle }}.jpg)| 36 | {% endfor %} 37 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/devtools.js: -------------------------------------------------------------------------------- 1 | (self["webpackChunkbrowser_extension"] = self["webpackChunkbrowser_extension"] || []).push([[893],{ 2 | 3 | /***/ 968: 4 | /***/ (() => { 5 | 6 | /** 7 | * This file is part of Adguard Browser Extension (https://github.com/AdguardTeam/AdguardBrowserExtension). 8 | * 9 | * Adguard Browser Extension is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Adguard Browser Extension is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with Adguard Browser Extension. If not, see . 21 | */ 22 | const browser = window.browser || chrome; // TODO: Try to move it to first cell 23 | 24 | browser.devtools.panels.elements.createSidebarPane('AdGuard', sidebar => { 25 | sidebar.setHeight('400px'); 26 | sidebar.setPage('../pages/devtools-elements-sidebar.html'); 27 | }); 28 | 29 | /***/ }) 30 | 31 | }, 32 | /******/ __webpack_require__ => { // webpackRuntimeModules 33 | /******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) 34 | /******/ var __webpack_exports__ = (__webpack_exec__(968)); 35 | /******/ } 36 | ]); -------------------------------------------------------------------------------- /_site/_templates/site_detail.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | orphan: true 3 | --- 4 | 5 | # {{ site.name }} 6 | 7 | ![{{ site.name }}](https://archive.org/download/latest-homepages/{{ site.handle|lower }}.jpg) 8 | 9 | | Attribute | Value | 10 | | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | Site | [{{ site.url }}]({{ site.url }}) | 12 | | Twitter handle | [@{{ site.handle }}](https://www.twitter.com/{{ site.handle }}) | 13 | | Location | {{ site.location }} | 14 | | Timezone | {{ site.timezone }} | 15 | | Country | [{{ site.country_name }}](https://palewi.re/docs/news-homepages/countries/{{ site.country|lower }}.html) 16 | | Language | [{{ site.language_name }}](https://palewi.re/docs/news-homepages/languages/{{ site.language|lower }}.html) 17 | {% if site.bundle_list %}| Bundles | {% for b in site.bundle_list %}{{ b.name }}{% if not loop.last %}, {% endif %} {% endfor %} |{% endif %} 18 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/toggler-bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | checked 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /_site/_static/custom.css: -------------------------------------------------------------------------------- 1 | table.align-default { 2 | margin-left: 0 !important; 3 | margin-right: 0 !important; 4 | } 5 | 6 | .latest-parent { 7 | display: flex; 8 | flex-wrap: wrap; 9 | justify-content: flex-start; 10 | } 11 | 12 | .latest-child { 13 | width: 212px; 14 | margin: 0 8px 8px 0; 15 | } 16 | 17 | .latest-child p { 18 | margin-top: 0; 19 | } 20 | 21 | @media (max-width: 550px) { 22 | .latest-child { 23 | width: 190px; 24 | } 25 | } 26 | 27 | .table-container { 28 | overflow-x: auto; 29 | } 30 | 31 | table.drudge-table { 32 | font-size: 16px; 33 | line-height: 22px; 34 | border: 0 !important; 35 | box-shadow: none !important; 36 | width: 100%; 37 | } 38 | 39 | table.drudge-table th { 40 | border: 0; 41 | padding-bottom: 0; 42 | } 43 | 44 | table.drudge-table td, table.drudge-table th { 45 | border-right: 0; 46 | border-left: 0; 47 | border-top: 0; 48 | } 49 | 50 | table.drudge-table tbody td { 51 | border-bottom: 0; 52 | padding: 0.5em 0.7em; 53 | vertical-align: bottom; 54 | } 55 | 56 | table.drudge-entities tbody td:first-of-type { 57 | padding-top: 0; 58 | } 59 | 60 | table.drudge-table tbody tr { 61 | border-bottom: 1px #e5e5e5 solid; 62 | } 63 | 64 | table.drudge-table tbody tr:last-of-type { 65 | border-bottom: 0 !important; 66 | } 67 | 68 | .drudge-table .rank { 69 | width: 40px; 70 | padding-left: 0; 71 | } 72 | 73 | .drudge-table .trend-chart { 74 | line-height: 16px; 75 | padding-top: 5px; 76 | } 77 | 78 | .trend-axis-label { 79 | font-size: 9px; 80 | width: 168px; 81 | } 82 | 83 | .trend-axis-right { 84 | float: right; 85 | } 86 | -------------------------------------------------------------------------------- /_site/gettingstarted.md: -------------------------------------------------------------------------------- 1 | # Installing the repository 2 | 3 | ```{contents} Sections 4 | :local: 5 | :depth: 1 6 | ``` 7 | 8 | ## Code 9 | 10 | Fork the [palewire/news-homepages](https://github.com/palewire/news-homepages) repository on GitHub and clone it on your computer. Move into the code directory and install the Python dependencies. 11 | 12 | ```bash 13 | pipenv install --dev 14 | ``` 15 | 16 | Install pre-commit hooks. 17 | 18 | ```bash 19 | pipenv run pre-commit install 20 | ``` 21 | 22 | Install Chrome for our web scraper. 23 | 24 | ```bash 25 | pipenv run playwright install --with-deps chromium 26 | ``` 27 | 28 | You're ready to work. Try a screenshot with the `screenshot.py` command. As with other commands, it expects you pass in the unique handle of the target site. The supported sites are listed in [`newshomepages/sources/sites.csv`](https://github.com/palewire/news-homepages/blob/main/newshomepages/sources/sites.csv). We use them as a unique identifier across the project. 29 | 30 | ```bash 31 | pipenv run python -m newshomepages.screenshot latimes 32 | ``` 33 | 34 | ## Environment variables 35 | 36 | Some of the commands require that you set environment variables with secret credentials. I recommend you create a `.env` file at the root of the project for use with pipenv. Here's all of them. 37 | 38 | ### Discord 39 | 40 | ``` 41 | DISCORD_BOT_TOKEN= 42 | ``` 43 | 44 | ### Slack 45 | 46 | ``` 47 | SLACK_WEBHOOK_URL= 48 | ``` 49 | 50 | ### Twitter 51 | 52 | ``` 53 | TWITTER_CONSUMER_KEY= 54 | TWITTER_CONSUMER_SECRET= 55 | TWITTER_ACCESS_TOKEN_KEY= 56 | TWITTER_ACCESS_TOKEN_SECRET= 57 | ``` 58 | 59 | ### The Internet Archive 60 | 61 | ``` 62 | IA_ACCESS_KEY= 63 | IA_SECRET_KEY= 64 | IA_COLLECTION= 65 | ``` 66 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 18 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /newshomepages/extract/wayback.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import click 4 | import pandas as pd 5 | from rich import print 6 | 7 | from .. import utils 8 | from .utils import _get_json_url 9 | 10 | 11 | @click.group() 12 | def cli(): 13 | """Download and parse the provided site's Wayback Machine files.""" 14 | pass 15 | 16 | 17 | @cli.command() 18 | @click.argument("handle") 19 | def wayback(handle): 20 | """Download and parse the provided site's Wayback Machine files.""" 21 | # Get the site data 22 | site = utils.get_site(handle) 23 | 24 | # Get all files 25 | wayback_df = utils.get_wayback_df() 26 | 27 | # Filter it down to files for the provided site 28 | site_df = wayback_df[wayback_df.handle == site["handle"]] 29 | print(f"{len(site_df)} wayback files found") 30 | 31 | # Read in the output file 32 | output_path = utils.THIS_DIR / f"{site['handle']}-wayback.csv" 33 | try: 34 | output_df = pd.read_csv(output_path) 35 | downloaded_files = set(output_df.file_url.unique()) 36 | except FileNotFoundError: 37 | output_df = pd.DataFrame() 38 | downloaded_files = set() 39 | 40 | # See how many files we don't have yet 41 | archived_files = set(site_df.url.unique()) 42 | missing_files = list(archived_files - downloaded_files) 43 | print(f"{len(missing_files)} files need to be download") 44 | 45 | # Quit if there's nothing there 46 | if not len(missing_files): 47 | return 48 | 49 | # Go get the files 50 | for url in missing_files: 51 | df = _get_json_url(url) 52 | output_df = pd.concat([output_df, df]) 53 | time.sleep(1) 54 | 55 | print(f":pencil: Writing {len(output_df)} rows to {output_path}") 56 | output_df.to_csv(output_path, index=False) 57 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/logo-shield.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 9 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /newshomepages/extract/accessibility.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import click 4 | import pandas as pd 5 | from rich import print 6 | 7 | from .. import utils 8 | from .utils import _get_json_url 9 | 10 | 11 | @click.group() 12 | def cli(): 13 | """Download and parse the provided site's accessibility files.""" 14 | pass 15 | 16 | 17 | @cli.command() 18 | @click.argument("handle") 19 | def accessibility(handle): 20 | """Download and parse the provided site's accessibility files.""" 21 | # Get the site data 22 | site = utils.get_site(handle) 23 | 24 | # Get all hyperlinks 25 | accessibility_df = utils.get_accessibility_df() 26 | 27 | # Filter it down to files for the provided site 28 | site_df = accessibility_df[accessibility_df.handle == site["handle"]] 29 | print(f"{len(site_df)} accessibility files found") 30 | 31 | # Read in the output file 32 | output_path = utils.THIS_DIR / f"{site['handle']}-accessibility.csv" 33 | try: 34 | output_df = pd.read_csv(output_path) 35 | downloaded_files = set(output_df.file_url.unique()) 36 | except FileNotFoundError: 37 | output_df = pd.DataFrame() 38 | downloaded_files = set() 39 | 40 | # See how many files we don't have yet 41 | archived_files = set(site_df.url.unique()) 42 | missing_files = list(archived_files - downloaded_files) 43 | print(f"{len(missing_files)} files need to be download") 44 | 45 | # Quit if there's nothing there 46 | if not len(missing_files): 47 | return 48 | 49 | # Go get the files 50 | for url in missing_files: 51 | df = _get_json_url(url) 52 | output_df = pd.concat([output_df, df]) 53 | time.sleep(1) 54 | 55 | print(f":pencil: Writing {len(output_df)} rows to {output_path}") 56 | output_df.to_csv(output_path, index=False) 57 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/reload-ico.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 20 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /_site/reference.rst: -------------------------------------------------------------------------------- 1 | ######### 2 | Reference 3 | ######### 4 | 5 | Documentation for a selection of our system’s common internal tools 6 | 7 | .. contents:: Table of contents 8 | :depth: 2 9 | :local: 10 | 11 | Commands 12 | ######## 13 | 14 | .. click:: newshomepages.accessibility:cli 15 | :prog: accessibility 16 | :nested: full 17 | 18 | .. click:: newshomepages.adstxt:cli 19 | :prog: adstxt 20 | :nested: full 21 | 22 | .. click:: newshomepages.analyze:cli 23 | :prog: analyze 24 | :nested: full 25 | 26 | .. click:: newshomepages.archive:cli 27 | :prog: archive 28 | :nested: full 29 | 30 | .. click:: newshomepages.batch:cli 31 | :prog: batch 32 | :nested: full 33 | 34 | .. click:: newshomepages.discorder:cli 35 | :prog: discorder 36 | :nested: full 37 | 38 | .. click:: newshomepages.site:cli 39 | :prog: site 40 | :nested: full 41 | 42 | .. click:: newshomepages.extract:cli 43 | :prog: extract 44 | :nested: full 45 | 46 | .. click:: newshomepages.hyperlinks:cli 47 | :prog: hyperlinks 48 | :nested: full 49 | 50 | .. click:: newshomepages.mosaic:cli 51 | :prog: mosaic 52 | :nested: full 53 | 54 | .. click:: newshomepages.robotstxt:cli 55 | :prog: robotstxt 56 | :nested: full 57 | 58 | .. click:: newshomepages.screenshot:cli 59 | :prog: screenshot 60 | :nested: full 61 | 62 | .. click:: newshomepages.slack:cli 63 | :prog: slack 64 | :nested: full 65 | 66 | .. click:: newshomepages.toot:cli 67 | :prog: toot 68 | :nested: full 69 | 70 | .. click:: newshomepages.wayback:cli 71 | :prog: wayback 72 | :nested: full 73 | 74 | Utilities 75 | ######### 76 | 77 | The `utils `_ module contains a variety of functions used by our commands. 78 | 79 | .. automodule:: newshomepages.utils 80 | :members: 81 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/css/layout.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | html, body, div, span, applet, object, iframe, 3 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 4 | a, abbr, acronym, address, big, cite, code, 5 | del, dfn, em, img, ins, kbd, q, s, samp, 6 | small, strike, strong, sub, sup, tt, var, 7 | b, u, i, center, 8 | dl, dt, dd, ol, ul, li, 9 | fieldset, form, label, legend, 10 | table, caption, tbody, tfoot, thead, tr, th, td, 11 | article, aside, canvas, details, embed, 12 | figure, figcaption, footer, header, hgroup, 13 | menu, nav, output, ruby, section, summary, 14 | time, mark, audio, video { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: 100%; 19 | font: inherit; 20 | vertical-align: baseline; 21 | } 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | ol, ul { 27 | list-style: none; 28 | } 29 | blockquote, q { 30 | quotes: none; 31 | } 32 | blockquote:before, blockquote:after, 33 | q:before, q:after { 34 | content: ''; 35 | content: none; 36 | } 37 | table { 38 | border-collapse: collapse; 39 | border-spacing: 0; 40 | } 41 | :root { 42 | font-size: 16px; 43 | } 44 | * { 45 | -webkit-box-sizing: border-box; 46 | box-sizing: border-box; 47 | } 48 | html { 49 | padding: 0; 50 | margin: 0; 51 | height: 100%; 52 | } 53 | body { 54 | background-color: #fff; 55 | font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Arial, sans-serif; 56 | padding: 0; 57 | margin: 0; 58 | font-size: 12px; 59 | height: 100%; 60 | line-height: 1; 61 | overflow-y: scroll; 62 | } 63 | 64 | a { 65 | color: currentColor; 66 | } 67 | 68 | button, a, select, span, label, .toggler-wr { 69 | outline: none; 70 | } 71 | 72 | .hidden{ 73 | display: none !important; 74 | } -------------------------------------------------------------------------------- /newshomepages/accessibility.py: -------------------------------------------------------------------------------- 1 | """Save the accessiblity tree of the provided site.""" 2 | 3 | from __future__ import annotations 4 | 5 | import json 6 | from pathlib import Path 7 | 8 | import click 9 | from playwright.sync_api import sync_playwright 10 | from playwright.sync_api._generated import BrowserContext 11 | from retry import retry 12 | from rich import print 13 | 14 | from . import utils 15 | 16 | 17 | @click.command() 18 | @click.argument("handle") 19 | @click.option("-o", "--output-dir", "output_dir", default="./") 20 | @click.option("--verbose", "verbose", default=False, is_flag=True) 21 | def cli(handle, output_dir, verbose=False): 22 | """Save the accessiblity tree of the provided site.""" 23 | # Get metadata 24 | site = utils.get_site(handle) 25 | 26 | # Set the output path 27 | output_path = Path(output_dir) / f"{site['handle']}.accessibility.json" 28 | output_path.parent.mkdir(parents=True, exist_ok=True) 29 | 30 | # Do the thing 31 | if verbose: 32 | print(f":newspaper: Fetching a11y tree from {site['url']}") 33 | with sync_playwright() as p: 34 | context = utils._load_persistent_context(p) 35 | _get_accessibility(context, site["url"], site["handle"], output_path) 36 | context.close() 37 | 38 | 39 | @retry(tries=3, delay=5, backoff=2) 40 | def _get_accessibility( 41 | context: BrowserContext, url: str, handle: str, output_path: Path 42 | ): 43 | """Run a command that fetches the accessibility tree for the provided site.""" 44 | with open(output_path, "w") as fp: 45 | page = utils._load_new_page_disable_javascript( 46 | context=context, 47 | url=url, 48 | handle=handle, 49 | ) 50 | snapshot = page.accessibility.snapshot() 51 | page.close() 52 | fp.write(json.dumps(snapshot, indent=4)) 53 | fp.write("\n") 54 | 55 | 56 | if __name__ == "__main__": 57 | cli() 58 | -------------------------------------------------------------------------------- /.github/workflows/site.yml: -------------------------------------------------------------------------------- 1 | name: Update website 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | concurrency: 9 | group: site 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | name: Build and deploy 15 | runs-on: ubuntu-latest 16 | steps: 17 | - id: checkout 18 | name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - id: setup-python 22 | name: Setup Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: '3.10' 26 | cache: 'pipenv' 27 | 28 | - id: install-pipenv 29 | name: Install pipenv 30 | run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python 31 | shell: bash 32 | 33 | - id: install-python-dependencies 34 | name: Install Python dependencies 35 | run: pipenv sync --dev 36 | shell: bash 37 | 38 | - id: render-templates 39 | name: Render site templates 40 | run: pipenv run make site 41 | shell: bash 42 | 43 | - id: build-site 44 | name: Build site 45 | run: cd _site && pipenv run make html 46 | shell: bash 47 | 48 | - id: configure-aws 49 | name: Configure AWS Credentials 50 | uses: aws-actions/configure-aws-credentials@v4 51 | with: 52 | aws-access-key-id: ${{ secrets.PALEWIRE_DOCS_AWS_ACCESS_KEY_ID }} 53 | aws-secret-access-key: ${{ secrets.PALEWIRE_DOCS_AWS_SECRET_ACCESS_KEY }} 54 | aws-region: us-east-1 55 | 56 | - id: npm-deploy 57 | name: Upload the prepared files 58 | uses: datadesk/delivery-deploy-action@v1 59 | with: 60 | bucket: ${{ secrets.PALEWIRE_DOCS_AWS_BUCKET }} 61 | base-path: news-homepages 62 | dir: _site/_build/html/ 63 | should-cache: false 64 | use-accelerate-endpoint: false 65 | public: true 66 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v5.0.0 6 | hooks: 7 | - id: trailing-whitespace 8 | exclude: newshomepages/extensions/ 9 | - id: end-of-file-fixer 10 | exclude: newshomepages/extensions/ 11 | - id: check-yaml 12 | exclude: newshomepages/extensions/ 13 | - id: check-added-large-files 14 | args: ['--maxkb=10000'] 15 | - id: check-byte-order-marker 16 | exclude: newshomepages/extensions 17 | - id: check-case-conflict 18 | exclude: newshomepages/extensions/ 19 | - id: check-json 20 | exclude: newshomepages/extensions/ 21 | - id: mixed-line-ending 22 | exclude: newshomepages/extensions/ 23 | 24 | - repo: https://github.com/psf/black 25 | rev: 25.1.0 26 | hooks: 27 | - id: black 28 | exclude: newshomepages/extensions/ 29 | 30 | - repo: https://github.com/asottile/blacken-docs 31 | rev: 1.19.1 32 | hooks: 33 | - id: blacken-docs 34 | additional_dependencies: [black] 35 | exclude: newshomepages/extensions/ 36 | 37 | - repo: https://github.com/timothycrosley/isort 38 | rev: 6.0.1 39 | hooks: 40 | - id: isort 41 | 42 | - repo: https://github.com/pycqa/flake8 43 | rev: 7.2.0 44 | hooks: 45 | - id: flake8 46 | additional_dependencies: 47 | - flake8-docstrings 48 | - flake8-bugbear 49 | 50 | - repo: https://github.com/asottile/pyupgrade 51 | rev: v3.20.0 52 | hooks: 53 | - id: pyupgrade 54 | args: [--py37-plus] 55 | 56 | - repo: https://github.com/pre-commit/mirrors-mypy 57 | rev: 'v1.16.0' # Use the sha / tag you want to point at 58 | hooks: 59 | - id: mypy 60 | exclude: newshomepages/extensions 61 | additional_dependencies: 62 | - types-requests 63 | - types-retry 64 | - types-python-slugify 65 | - types-pytz 66 | - types-PyYAML 67 | -------------------------------------------------------------------------------- /_site/index.md: -------------------------------------------------------------------------------- 1 | # homepages.news 2 | 3 | An open-source archive that gathers, saves, shares and analyzes news homepages 4 | 5 | ## Features 6 | 7 | ```{toctree} 8 | :maxdepth: 1 9 | :name: mastertoc 10 | 11 | latest 12 | accessibility 13 | performance 14 | drudge 15 | openai-gptbot-robotstxt 16 | extracts 17 | status-report 18 | ``` 19 | 20 | ## Directory 21 | 22 | ```{toctree} 23 | :maxdepth: 1 24 | :name: directory 25 | 26 | sources 27 | countries 28 | languages 29 | bundles 30 | ``` 31 | 32 | ## About 33 | 34 | The archive at [homepages.news](https://homepages.news) is an [open-source software project](https://github.com/palewire/news-homepages) managed by [Ben Welsh](https://palewi.re/who-is-ben-welsh/). Each day it gathers screenshots, accessibility trees, hyperlink lists, robots.txt and Lighthouse audits from hundreds of news homepages around the world. It also ensures that all sites are routinely saved by the [Wayback Machine at archive.org](https://web.archive.org/). 35 | 36 | The assets are archived in a permanent collection [at the Internet Archive](https://archive.org/details/news-homepages). The latest screenshots, analysis and data are published here, as well as on [Mastodon](https://mastodon.palewi.re/@newshomepages). 37 | 38 | The system supports the creation of bots to post a newsroom’s latest screenshots into a private Slack channel. [The tool](https://palewi.re/docs/news-homepages/slack.html) is used by organizations in the U.S. and abroad to save and share images each day. 39 | 40 | ## Contributing 41 | 42 | ```{toctree} 43 | :maxdepth: 1 44 | :name: contributing 45 | 46 | gettingstarted 47 | adding 48 | slack 49 | reference 50 | ``` 51 | 52 | ## Links 53 | 54 | - Internet Archive: [archive.org/details/news-homepages](https://archive.org/details/news-homepages) 55 | - Mastodon: [@newshomepages](https://mastodon.palewi.re/@newshomepages) 56 | - Code: [github.com/palewire/news-homepages](https://github.com/palewire/news-homepages) 57 | - Task runner: [github.com/palewire/news-homepages/actions](https://github.com/palewire/news-homepages/actions) 58 | - Packaging: [pypi.org/project/newshomepages](https://pypi.org/project/newshomepages/) 59 | - Issues: [github.com/palewire/news-homepages/issues](https://github.com/palewire/news-homepages/issues) 60 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/pages/devtools-elements-sidebar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/add-a-site.yaml: -------------------------------------------------------------------------------- 1 | name: Add a site 2 | description: Suggest a news homepage for inclusion 3 | title: "[Add site]: " 4 | labels: 5 | - enhancement 6 | - good first issue 7 | - help wanted 8 | assignees: 9 | - palewire 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: | 14 | The process for adding a site is spelled out in [our documentation](https://palewi.re/docs/news-homepages/adding.html). Users who aren't comfortable suggesting the change via a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) can submit a request here. 15 | - type: input 16 | id: handle 17 | attributes: 18 | label: X.com handle 19 | description: What is the site's handle on X? Without an @ prefix, please. 20 | validations: 21 | required: true 22 | - type: input 23 | id: url 24 | attributes: 25 | label: Homepage URL 26 | description: What is the site's homepage URL? Include the https:// prefix, please. 27 | validations: 28 | required: true 29 | - type: input 30 | id: name 31 | attributes: 32 | label: Name 33 | description: What is the site's name? 34 | validations: 35 | required: true 36 | - type: input 37 | id: location 38 | attributes: 39 | label: Location 40 | description: What is the location of the newsroom's headquarters? 41 | validations: 42 | required: false 43 | - type: input 44 | id: timezone 45 | attributes: 46 | label: Timezone 47 | description: What is the timezone of the newsroom's headquarters? A list of all accepted timezones can be found [here](https://gist.github.com/heyalexej/8bf688fd67d7199be4a1682b3eec7568). 48 | validations: 49 | required: false 50 | - type: input 51 | id: country 52 | attributes: 53 | label: Country 54 | description: What is the country of the newsroom's headquarters? Please submit the two-letter code from [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). 55 | validations: 56 | required: false 57 | - type: input 58 | id: language 59 | attributes: 60 | label: Language 61 | description: What is the primary language of the newsroom's homepage? Please submit the two-letter code from [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1). 62 | validations: 63 | required: false 64 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/prebid-ads.js: -------------------------------------------------------------------------------- 1 | (function(source, args){ 2 | function prebidAds(source) { 3 | window.canRunAds = true; 4 | window.isAdBlockActive = false; 5 | hit(source); 6 | } 7 | function hit(source, message) { 8 | if (source.verbose !== true) { 9 | return; 10 | } 11 | 12 | try { 13 | var log = console.log.bind(console); 14 | var trace = console.trace.bind(console); // eslint-disable-line compat/compat 15 | 16 | var prefix = source.ruleText || ''; 17 | 18 | if (source.domainName) { 19 | var AG_SCRIPTLET_MARKER = '#%#//'; 20 | var UBO_SCRIPTLET_MARKER = '##+js'; 21 | var ruleStartIndex; 22 | 23 | if (source.ruleText.indexOf(AG_SCRIPTLET_MARKER) > -1) { 24 | ruleStartIndex = source.ruleText.indexOf(AG_SCRIPTLET_MARKER); 25 | } else if (source.ruleText.indexOf(UBO_SCRIPTLET_MARKER) > -1) { 26 | ruleStartIndex = source.ruleText.indexOf(UBO_SCRIPTLET_MARKER); 27 | } // delete all domains from ruleText and leave just rule part 28 | 29 | 30 | var rulePart = source.ruleText.slice(ruleStartIndex); // prepare applied scriptlet rule for specific domain 31 | 32 | prefix = "".concat(source.domainName).concat(rulePart); 33 | } // Used to check if scriptlet uses 'hit' function for logging 34 | 35 | 36 | var LOG_MARKER = 'log: '; 37 | 38 | if (message) { 39 | if (message.indexOf(LOG_MARKER) === -1) { 40 | log("".concat(prefix, " message:\n").concat(message)); 41 | } else { 42 | log(message.slice(LOG_MARKER.length)); 43 | } 44 | } 45 | 46 | log("".concat(prefix, " trace start")); 47 | 48 | if (trace) { 49 | trace(); 50 | } 51 | 52 | log("".concat(prefix, " trace end")); 53 | } catch (e) {// try catch for Edge 15 54 | // In according to this issue https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14495220/ 55 | // console.log throws an error 56 | } // This is necessary for unit-tests only! 57 | 58 | 59 | if (typeof window.__debug === 'function') { 60 | window.__debug(source); 61 | } 62 | }; 63 | const updatedArgs = args ? [].concat(source).concat(args) : [source]; 64 | try { 65 | prebidAds.apply(this, updatedArgs); 66 | } catch (e) { 67 | console.log(e); 68 | } 69 | 70 | })({"name":"prebid-ads","args":[]}, []); -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/noeval.js: -------------------------------------------------------------------------------- 1 | (function(source, args){ 2 | function noeval(source) { 3 | window.eval = function evalWrapper(s) { 4 | hit(source, "AdGuard has prevented eval:\n".concat(s)); 5 | }.bind(); 6 | } 7 | function hit(source, message) { 8 | if (source.verbose !== true) { 9 | return; 10 | } 11 | 12 | try { 13 | var log = console.log.bind(console); 14 | var trace = console.trace.bind(console); // eslint-disable-line compat/compat 15 | 16 | var prefix = source.ruleText || ''; 17 | 18 | if (source.domainName) { 19 | var AG_SCRIPTLET_MARKER = '#%#//'; 20 | var UBO_SCRIPTLET_MARKER = '##+js'; 21 | var ruleStartIndex; 22 | 23 | if (source.ruleText.indexOf(AG_SCRIPTLET_MARKER) > -1) { 24 | ruleStartIndex = source.ruleText.indexOf(AG_SCRIPTLET_MARKER); 25 | } else if (source.ruleText.indexOf(UBO_SCRIPTLET_MARKER) > -1) { 26 | ruleStartIndex = source.ruleText.indexOf(UBO_SCRIPTLET_MARKER); 27 | } // delete all domains from ruleText and leave just rule part 28 | 29 | 30 | var rulePart = source.ruleText.slice(ruleStartIndex); // prepare applied scriptlet rule for specific domain 31 | 32 | prefix = "".concat(source.domainName).concat(rulePart); 33 | } // Used to check if scriptlet uses 'hit' function for logging 34 | 35 | 36 | var LOG_MARKER = 'log: '; 37 | 38 | if (message) { 39 | if (message.indexOf(LOG_MARKER) === -1) { 40 | log("".concat(prefix, " message:\n").concat(message)); 41 | } else { 42 | log(message.slice(LOG_MARKER.length)); 43 | } 44 | } 45 | 46 | log("".concat(prefix, " trace start")); 47 | 48 | if (trace) { 49 | trace(); 50 | } 51 | 52 | log("".concat(prefix, " trace end")); 53 | } catch (e) {// try catch for Edge 15 54 | // In according to this issue https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14495220/ 55 | // console.log throws an error 56 | } // This is necessary for unit-tests only! 57 | 58 | 59 | if (typeof window.__debug === 'function') { 60 | window.__debug(source); 61 | } 62 | }; 63 | const updatedArgs = args ? [].concat(source).concat(args) : [source]; 64 | try { 65 | noeval.apply(this, updatedArgs); 66 | } catch (e) { 67 | console.log(e); 68 | } 69 | 70 | })({"name":"noeval","args":[]}, []); -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/naver-wcslog.js: -------------------------------------------------------------------------------- 1 | (function(source, args){ 2 | function NaverWcslog(source) { 3 | window.wcs_add = {}; 4 | window.wcs_do = noopFunc; 5 | window.wcs = { 6 | inflow: noopFunc 7 | }; 8 | hit(source); 9 | } 10 | function hit(source, message) { 11 | if (source.verbose !== true) { 12 | return; 13 | } 14 | 15 | try { 16 | var log = console.log.bind(console); 17 | var trace = console.trace.bind(console); // eslint-disable-line compat/compat 18 | 19 | var prefix = source.ruleText || ''; 20 | 21 | if (source.domainName) { 22 | var AG_SCRIPTLET_MARKER = '#%#//'; 23 | var UBO_SCRIPTLET_MARKER = '##+js'; 24 | var ruleStartIndex; 25 | 26 | if (source.ruleText.indexOf(AG_SCRIPTLET_MARKER) > -1) { 27 | ruleStartIndex = source.ruleText.indexOf(AG_SCRIPTLET_MARKER); 28 | } else if (source.ruleText.indexOf(UBO_SCRIPTLET_MARKER) > -1) { 29 | ruleStartIndex = source.ruleText.indexOf(UBO_SCRIPTLET_MARKER); 30 | } // delete all domains from ruleText and leave just rule part 31 | 32 | 33 | var rulePart = source.ruleText.slice(ruleStartIndex); // prepare applied scriptlet rule for specific domain 34 | 35 | prefix = "".concat(source.domainName).concat(rulePart); 36 | } // Used to check if scriptlet uses 'hit' function for logging 37 | 38 | 39 | var LOG_MARKER = 'log: '; 40 | 41 | if (message) { 42 | if (message.indexOf(LOG_MARKER) === -1) { 43 | log("".concat(prefix, " message:\n").concat(message)); 44 | } else { 45 | log(message.slice(LOG_MARKER.length)); 46 | } 47 | } 48 | 49 | log("".concat(prefix, " trace start")); 50 | 51 | if (trace) { 52 | trace(); 53 | } 54 | 55 | log("".concat(prefix, " trace end")); 56 | } catch (e) {// try catch for Edge 15 57 | // In according to this issue https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14495220/ 58 | // console.log throws an error 59 | } // This is necessary for unit-tests only! 60 | 61 | 62 | if (typeof window.__debug === 'function') { 63 | window.__debug(source); 64 | } 65 | } 66 | function noopFunc() {}; 67 | const updatedArgs = args ? [].concat(source).concat(args) : [source]; 68 | try { 69 | NaverWcslog.apply(this, updatedArgs); 70 | } catch (e) { 71 | console.log(e); 72 | } 73 | 74 | })({"name":"naver-wcslog","args":[]}, []); -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/assets/images/chrome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 12 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.0.178", 3 | "manifest_version": 2, 4 | "name": "__MSG_name__", 5 | "short_name": "__MSG_short_name__", 6 | "author": "Adguard Software Ltd", 7 | "default_locale": "en", 8 | "description": "__MSG_description__", 9 | "icons": { 10 | "16": "assets/icons/green-16.png", 11 | "128": "assets/icons/green-128.png" 12 | }, 13 | "browser_action": { 14 | "default_icon": { 15 | "19": "assets/icons/green-19.png", 16 | "38": "assets/icons/green-38.png" 17 | }, 18 | "default_title": "__MSG_name__", 19 | "default_popup": "pages/popup.html" 20 | }, 21 | "background": { 22 | "page": "pages/background.html", 23 | "persistent": true 24 | }, 25 | "content_scripts": [ 26 | { 27 | "all_frames": true, 28 | "js": [ 29 | "pages/content-script-start.js" 30 | ], 31 | "matches": [ 32 | "http://*/*", 33 | "https://*/*" 34 | ], 35 | "match_about_blank": true, 36 | "run_at": "document_start" 37 | }, 38 | { 39 | "all_frames": true, 40 | "js": [ 41 | "pages/content-script-end.js" 42 | ], 43 | "matches": [ 44 | "http://*/*", 45 | "https://*/*" 46 | ], 47 | "match_about_blank": true, 48 | "run_at": "document_end" 49 | }, 50 | { 51 | "all_frames": false, 52 | "js": [ 53 | "pages/thankyou.js" 54 | ], 55 | "matches": [ 56 | "*://*.adguard.com/*/thankyou.html*" 57 | ], 58 | "run_at": "document_start" 59 | } 60 | ], 61 | "minimum_chrome_version": "79.0", 62 | "web_accessible_resources": [ 63 | "/web-accessible-resources/*" 64 | ], 65 | "options_page": "pages/options.html", 66 | "devtools_page": "pages/devtools.html", 67 | "permissions": [ 68 | "tabs", 69 | "", 70 | "webRequest", 71 | "webRequestBlocking", 72 | "webNavigation", 73 | "storage", 74 | "unlimitedStorage", 75 | "contextMenus", 76 | "cookies" 77 | ], 78 | "optional_permissions": [ 79 | "privacy" 80 | ], 81 | "content_security_policy": "script-src 'self' 'sha256-CA6h8X2PlfpQX4tMUn1T0JjKVVC7t6WCcAxnAPhndGk='; object-src 'self'" 82 | } 83 | -------------------------------------------------------------------------------- /newshomepages/site/performance_ranking.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import click 4 | import numpy as np 5 | import pandas as pd 6 | from rich import print 7 | 8 | from .. import utils 9 | from .utils import _write_template 10 | 11 | 12 | @click.group() 13 | def cli(): 14 | """Create page ranking sites by Lighthouse performance score.""" 15 | pass 16 | 17 | 18 | @cli.command() 19 | def performance_ranking(): 20 | """Create page ranking sites by Lighthouse performance score.""" 21 | # Read in our dataset 22 | performance_df = utils.get_extract_df( 23 | "lighthouse-analysis.csv", 24 | dtype={ 25 | "handle": str, 26 | "performance_median": float, 27 | "performance_color": str, 28 | "performance_rank": float, 29 | }, 30 | ) 31 | 32 | # Convert the rank to an integer 33 | performance_df.performance_rank = performance_df.performance_rank.astype(int) 34 | 35 | # Filter out nulls 36 | performance_df = performance_df[ 37 | ~pd.isnull(performance_df.performance_median) 38 | ].copy() 39 | 40 | # Calculate the grand total 41 | median = performance_df.performance_median.median() * 100 42 | 43 | # Generate a distribution for our chart 44 | def _round(val: float) -> int: 45 | return int(np.floor(np.floor(val * 1000) / 100) * 10) 46 | 47 | performance_df["performance_decile"] = performance_df.performance_median.apply( 48 | _round 49 | ) 50 | histogram_df = ( 51 | performance_df.groupby("performance_decile").size().rename("n").reset_index() 52 | ) 53 | histogram_df = histogram_df.merge( 54 | pd.DataFrame(range(0, 101, 10), columns=["performance_decile"]), 55 | how="right", 56 | on="performance_decile", 57 | ).fillna(0) 58 | 59 | # Prep the ranking table 60 | performance_df.performance_median = performance_df.performance_median * 100 61 | performance_df.performance_median = performance_df.performance_median.astype(int) 62 | site_df = utils.get_site_df() 63 | merged_df = site_df.merge(performance_df, on="handle", how="inner") 64 | 65 | # Create the page 66 | site_list = merged_df.sort_values(["performance_rank", "name"]).to_dict( 67 | orient="records" 68 | ) 69 | print( 70 | f":abacus: Creating performance ranking page for {len(site_list)} qualified sites" 71 | ) 72 | context = dict( 73 | median=median, 74 | histogram_json=json.dumps(histogram_df.to_dict(orient="records")), 75 | site_list=site_list, 76 | ) 77 | _write_template("performance.md", context) 78 | -------------------------------------------------------------------------------- /newshomepages/extensions/adguard/web-accessible-resources/redirects/scorecardresearch-beacon.js: -------------------------------------------------------------------------------- 1 | (function(source, args){ 2 | function ScoreCardResearchBeacon(source) { 3 | window.COMSCORE = { 4 | purge: function purge() { 5 | // eslint-disable-next-line no-underscore-dangle 6 | window._comscore = []; 7 | }, 8 | beacon: function beacon() {} 9 | }; 10 | hit(source); 11 | } 12 | function hit(source, message) { 13 | if (source.verbose !== true) { 14 | return; 15 | } 16 | 17 | try { 18 | var log = console.log.bind(console); 19 | var trace = console.trace.bind(console); // eslint-disable-line compat/compat 20 | 21 | var prefix = source.ruleText || ''; 22 | 23 | if (source.domainName) { 24 | var AG_SCRIPTLET_MARKER = '#%#//'; 25 | var UBO_SCRIPTLET_MARKER = '##+js'; 26 | var ruleStartIndex; 27 | 28 | if (source.ruleText.indexOf(AG_SCRIPTLET_MARKER) > -1) { 29 | ruleStartIndex = source.ruleText.indexOf(AG_SCRIPTLET_MARKER); 30 | } else if (source.ruleText.indexOf(UBO_SCRIPTLET_MARKER) > -1) { 31 | ruleStartIndex = source.ruleText.indexOf(UBO_SCRIPTLET_MARKER); 32 | } // delete all domains from ruleText and leave just rule part 33 | 34 | 35 | var rulePart = source.ruleText.slice(ruleStartIndex); // prepare applied scriptlet rule for specific domain 36 | 37 | prefix = "".concat(source.domainName).concat(rulePart); 38 | } // Used to check if scriptlet uses 'hit' function for logging 39 | 40 | 41 | var LOG_MARKER = 'log: '; 42 | 43 | if (message) { 44 | if (message.indexOf(LOG_MARKER) === -1) { 45 | log("".concat(prefix, " message:\n").concat(message)); 46 | } else { 47 | log(message.slice(LOG_MARKER.length)); 48 | } 49 | } 50 | 51 | log("".concat(prefix, " trace start")); 52 | 53 | if (trace) { 54 | trace(); 55 | } 56 | 57 | log("".concat(prefix, " trace end")); 58 | } catch (e) {// try catch for Edge 15 59 | // In according to this issue https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14495220/ 60 | // console.log throws an error 61 | } // This is necessary for unit-tests only! 62 | 63 | 64 | if (typeof window.__debug === 'function') { 65 | window.__debug(source); 66 | } 67 | }; 68 | const updatedArgs = args ? [].concat(source).concat(args) : [source]; 69 | try { 70 | ScoreCardResearchBeacon.apply(this, updatedArgs); 71 | } catch (e) { 72 | console.log(e); 73 | } 74 | 75 | })({"name":"scorecardresearch-beacon","args":[]}, []); -------------------------------------------------------------------------------- /newshomepages/adstxt.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pathlib import Path 4 | from urllib.parse import urlparse 5 | 6 | import click 7 | import requests 8 | from retry import retry 9 | from rich import print 10 | 11 | from . import utils 12 | 13 | 14 | @click.command() 15 | @click.argument("handle") 16 | @click.option("-o", "--output-dir", "output_dir", default="./") 17 | @click.option("--timeout", "timeout", default="5") 18 | @click.option("--verbose", "verbose", default=False, is_flag=True) 19 | def cli(handle: str, output_dir: str, timeout: str = "5", verbose: bool = False): 20 | """Save the raw ads.txt of the provided site.""" 21 | # Get the site 22 | site = utils.get_site(handle) 23 | 24 | # Get the ads.txt 25 | adstxt = _get_adstxt(site["url"], int(timeout), verbose=verbose) 26 | 27 | if adstxt is None: 28 | # If there is no ads.txt, we drop out now 29 | print(f":robot: No ads.txt for {site['handle']}") 30 | adstxt = "404: No file found" 31 | 32 | # Set the output path 33 | output_path = Path(output_dir) / f"{site['handle']}.ads.txt" 34 | output_path.parent.mkdir(parents=True, exist_ok=True) 35 | 36 | # Write it out 37 | if verbose: 38 | print(f":robot: Writing {output_path}") 39 | with output_path.open("w") as f: 40 | f.write(adstxt) 41 | 42 | 43 | @retry(tries=3, delay=15, backoff=2) 44 | def _get_adstxt( 45 | site_url: str, 46 | timeout: int = 5, 47 | verbose: bool = False, 48 | ) -> str | None: 49 | """Get the raw ads.txt for a site.""" 50 | # Create the ads.txt URL 51 | adstxt_url = ( 52 | urlparse(site_url) 53 | ._replace(path="") 54 | ._replace(query="") 55 | ._replace(path="ads.txt") 56 | .geturl() 57 | ) 58 | if verbose: 59 | print(f":robot: Fetching {adstxt_url}") 60 | 61 | # Set the headers 62 | headers = {"User-Agent": utils.get_user_agent()} 63 | 64 | # Make the request 65 | r = requests.get(adstxt_url, timeout=timeout, headers=headers) 66 | 67 | # Check if the request is a 404 68 | if r.status_code == 404: 69 | # In this case, there is no ads.txt 70 | # so we return None 71 | return None 72 | else: 73 | # Otherwise, we return the text, 74 | # after checking that the request was successful 75 | try: 76 | assert r.ok 77 | except AssertionError: 78 | msg = f"Request failed with status code {r.status_code}" 79 | if verbose: 80 | print(msg) 81 | raise AssertionError(msg) 82 | return r.text 83 | 84 | 85 | if __name__ == "__main__": 86 | cli() 87 | --------------------------------------------------------------------------------