Skip to content

Commit eb75f02

Browse files
author
TechStack Global
committed
fix: normalize amazon dp affiliate links + replace comparison tables (force)
1 parent 1a20143 commit eb75f02

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+6516
-4861
lines changed

blog.html

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@
4646
<meta content="Compare and choose the best hardware for your workspace." name="twitter:description" />
4747
<meta content="https://techstackglobal.github.io/assets/images/og-blog.jpg" name="twitter:image" />
4848

49-
<!-- Site Favicons -->
50-
<link rel="icon" href="/favicon.ico">
51-
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
52-
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
53-
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
54-
<meta name="theme-color" content="#0a2540">
55-
<meta property="og:site_name" content="TechStack Global">
56-
<meta name="application-name" content="TechStack Global">
57-
<script type="application/ld+json">
49+
<!-- Site Favicons -->
50+
<link rel="icon" href="/favicon.ico">
51+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
52+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
53+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
54+
<meta name="theme-color" content="#0a2540">
55+
<meta property="og:site_name" content="TechStack Global">
56+
<meta name="application-name" content="TechStack Global">
57+
<script type="application/ld+json">
5858
{
5959
"@context": "https://schema.org",
6060
"@type": "Organization",
@@ -103,6 +103,34 @@ <h1>Guides</h1>
103103
</div>
104104

105105
<div class="blog-grid" id="blog-posts-container">
106+
<!-- New: Headphones Online Classes Guide -->
107+
<div class="blog-card glass-card" style="border-top: 3px solid #ff4b2b;">
108+
<img src="posts/images/pillar-headphones-hero.jpg" loading="lazy"
109+
alt="Headphones for online classes thumbnail"
110+
style="max-width: 100%; height: auto; border-radius: 8px; margin-bottom: 1rem; display: block;">
111+
<div class="blog-category">Hardware Guide</div>
112+
<h3><a href="posts/best-headphones-for-online-classes-2026.html">Best Headphones for Online Classes
113+
(2026)</a></h3>
114+
<p>Discover the best headphones for online classes in 2026. We review the Sony WH-1000XM5, Bose
115+
QuietComfort Ultra, and Sennheiser Momentum 4 for perfect audio.</p>
116+
<div class="card-meta"><span>Hardware Guide</span> <a class="read-more"
117+
href="posts/best-headphones-for-online-classes-2026.html">Read Guide →</a></div>
118+
</div>
119+
120+
<!-- New: Ultrawide Monitor Programming Guide -->
121+
<div class="blog-card glass-card" style="border-top: 3px solid #00ff9d;">
122+
<img src="posts/images/pillar-monitors-hero.jpg" loading="lazy"
123+
alt="Ultrawide monitor for programming thumbnail"
124+
style="max-width: 100%; height: auto; border-radius: 8px; margin-bottom: 1rem; display: block;">
125+
<div class="blog-category">Hardware Guide</div>
126+
<h3><a href="posts/best-ultrawide-monitor-for-programming-2026.html">Best Ultrawide Monitor for
127+
Programming (2026)</a></h3>
128+
<p>We review the best ultrawide monitor for programming in 2026, comparing the Alienware AW3423DWF,
129+
Samsung Odyssey G8, and LG 34GP83A-B for developers.</p>
130+
<div class="card-meta"><span>Hardware Guide</span> <a class="read-more"
131+
href="posts/best-ultrawide-monitor-for-programming-2026.html">Read Guide →</a></div>
132+
</div>
133+
106134
<!-- New: Microphones Guide -->
107135
<div class="blog-card glass-card" style="border-top: 3px solid var(--accent);">
108136
<div class="blog-category">Hardware Guide</div>

create_mics.py

Lines changed: 508 additions & 0 deletions
Large diffs are not rendered by default.

docs/affiliate_links.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# TechStack Global Master Affiliate Links
2+
3+
This is a master reference list of stable Amazon affiliate links for products featured across the website.
4+
Always use the clean `amazon.com/dp/PRODUCTID?tag=YOURTAG` format to prevent links from breaking due to messy tracking parameters.
5+
6+
Sony WH-1000XM5
7+
https://www.amazon.com/dp/B09XS7JWHH?tag=techstackglob-20
8+
9+
Bose QC Ultra
10+
https://www.amazon.com/dp/B0CCZ26B5V?tag=techstackglob-20
11+
12+
Sennheiser Momentum 4
13+
https://www.amazon.com/dp/B0B6GHW1SX?tag=techstackglob-20

inject_safe_headers.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
import glob
3+
import re
4+
5+
html_files = glob.glob("*.html") + glob.glob("posts/*.html")
6+
7+
favicon_tags = [
8+
'<link rel="icon" href="/favicon.ico">',
9+
'<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">',
10+
'<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">',
11+
'<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">',
12+
'<meta name="theme-color" content="#0a2540">',
13+
'<meta property="og:site_name" content="TechStack Global">',
14+
'<meta name="application-name" content="TechStack Global">',
15+
]
16+
17+
org_schema = """<script type="application/ld+json">
18+
{
19+
"@context": "https://schema.org",
20+
"@type": "Organization",
21+
"name": "TechStack Global",
22+
"url": "https://techstackglobal.github.io/",
23+
"logo": "https://techstackglobal.github.io/favicon-32x32.png"
24+
}
25+
</script>"""
26+
27+
for filepath in html_files:
28+
with open(filepath, "r", encoding="utf-8") as f:
29+
content = f.read()
30+
31+
# 1. Favicon Tags
32+
tags_to_insert = []
33+
34+
# We only insert tags that DO NOT exist in the file
35+
for tag in favicon_tags:
36+
if tag not in content:
37+
# Special check for theme-color to avoid duplicates if other variations exist
38+
if "theme-color" in tag and "name=\"theme-color\"" in content:
39+
continue
40+
if "og:site_name" in tag and "property=\"og:site_name\"" in content:
41+
continue
42+
if "application-name" in tag and "name=\"application-name\"" in content:
43+
continue
44+
tags_to_insert.append(tag)
45+
46+
if tags_to_insert:
47+
# Prepend the comment if we are adding anything
48+
tags_to_insert.insert(0, "<!-- Site Favicons -->")
49+
block = "\n" + "\n".join(tags_to_insert) + "\n"
50+
content = content.replace("</head>", block + "</head>")
51+
52+
# 2. Org Schema
53+
if '"@type": "Organization"' not in content and '"@type":"Organization"' not in content:
54+
content = content.replace("</head>", org_schema + "\n</head>")
55+
56+
# 3. Homepage fixes ONLY for index.html
57+
if filepath == "index.html":
58+
# Enforce canonical
59+
if "rel=\"canonical\"" in content:
60+
content = re.sub(r'<link[^>]+rel=["\']canonical["\'][^>]*>', '<link rel="canonical" href="https://techstackglobal.github.io/index.html">', content)
61+
else:
62+
content = content.replace("</head>", '<link rel="canonical" href="https://techstackglobal.github.io/index.html">\n</head>')
63+
64+
# Enforce og:url
65+
if "og:url" in content:
66+
content = re.sub(r'<meta[^>]+property=["\']og:url["\'][^>]*>', '<meta property="og:url" content="https://techstackglobal.github.io/index.html">', content)
67+
else:
68+
content = content.replace("</head>", '<meta property="og:url" content="https://techstackglobal.github.io/index.html">\n</head>')
69+
70+
# Enforce title
71+
if "<title>" in content:
72+
content = re.sub(r'<title>.*?</title>', '<title>TechStack Global | Smarter Tech Decisions</title>', content, flags=re.IGNORECASE)
73+
else:
74+
content = content.replace("</head>", '<title>TechStack Global | Smarter Tech Decisions</title>\n</head>')
75+
76+
# Enforce og:title
77+
if "og:title" in content:
78+
content = re.sub(r'<meta[^>]+property=["\']og:title["\'][^>]*>', '<meta property="og:title" content="TechStack Global | Smarter Tech Decisions">', content)
79+
else:
80+
content = content.replace("</head>", '<meta property="og:title" content="TechStack Global | Smarter Tech Decisions">\n</head>')
81+
82+
with open(filepath, "w", encoding="utf-8") as f:
83+
f.write(content)
84+
85+
print("Headers injected successfully.")

my_check_links.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import os
2+
import urllib.parse
3+
from bs4 import BeautifulSoup
4+
5+
files_to_test = [
6+
r'c:\Users\PMLS\Desktop\Youtube Shorts\b2b_blog\posts\best-microphones-for-remote-work-2026.html',
7+
r'c:\Users\PMLS\Desktop\Youtube Shorts\b2b_blog\posts\best-headphones-for-zoom-meetings-2026.html'
8+
]
9+
base_dir = r'c:\Users\PMLS\Desktop\Youtube Shorts\b2b_blog'
10+
11+
for file_path in files_to_test:
12+
print(file_path)
13+
with open(file_path, 'r', encoding='utf-8') as f:
14+
soup = BeautifulSoup(f.read(), 'html.parser')
15+
for a in soup.find_all('a'):
16+
href = a.get('href', '')
17+
if href.startswith('http') or href.startswith('#') or href.startswith('mailto:') or not href:
18+
continue
19+
# Check local file
20+
href = href.split('?')[0].split('#')[0]
21+
if href.startswith('/'):
22+
target = os.path.join(base_dir, href[1:].replace('/', '\\'))
23+
else:
24+
target = os.path.join(os.path.dirname(file_path), href.replace('/', '\\'))
25+
target = os.path.normpath(target)
26+
if not os.path.exists(target):
27+
print(f'WARNING: Broken internal link: {href} -> {target}')
28+
print('Done link check')

my_verify.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import os
2+
from bs4 import BeautifulSoup
3+
import requests
4+
5+
files_to_test = [
6+
r'c:\Users\PMLS\Desktop\Youtube Shorts\b2b_blog\posts\best-microphones-for-remote-work-2026.html',
7+
r'c:\Users\PMLS\Desktop\Youtube Shorts\b2b_blog\posts\best-headphones-for-zoom-meetings-2026.html'
8+
]
9+
10+
dash_chars = ['—', '–']
11+
12+
for file_path in files_to_test:
13+
print(f'\\n--- Testing {os.path.basename(file_path)} ---')
14+
with open(file_path, 'r', encoding='utf-8') as f:
15+
html = f.read()
16+
soup = BeautifulSoup(html, 'html.parser')
17+
18+
# Check dashes
19+
for tag in ['p', 'h1', 'h2', 'h3', 'h4', 'li']:
20+
for el in soup.find_all(tag):
21+
text = el.get_text()
22+
for dash in dash_chars:
23+
if dash in text:
24+
print(f'WARNING: Dash {dash} found in text: {text[:50]}...')
25+
26+
# Check images
27+
for img in soup.find_all('img'):
28+
src = img.get('src', '')
29+
if not src.startswith('/posts/images/') and not src.startswith('../posts/images/'):
30+
print(f'WARNING: Invalid image source: {src}')
31+
32+
# we will check if it exists relative to the root
33+
abs_src = 'c:\\Users\\PMLS\\Desktop\\Youtube Shorts\\b2b_blog' + src.replace('/', '\\')
34+
if not os.path.exists(abs_src):
35+
print(f'WARNING: Image file missing locally: {abs_src}')
36+
37+
if img.get('loading') != 'lazy':
38+
print(f'WARNING: Missing loading=lazy on image: {src}')
39+
if not img.get('alt'):
40+
print(f'WARNING: Missing alt text on image: {src}')
41+
42+
# Check affiliate links
43+
for a in soup.find_all('a', class_='btn-primary'):
44+
href = a.get('href', '')
45+
if 'amazon.com' in href:
46+
if 'tag=techstackglob-20' not in href:
47+
print(f'WARNING: Missing affiliate tag in: {href}')
48+
if a.get('target') != '_blank':
49+
print(f'WARNING: Missing target=_blank in: {href}')
50+
if a.get('rel') != ['nofollow', 'noopener', 'sponsored']:
51+
print(f'WARNING: Invalid rel attribute in: {href}')
52+
53+
# Check tables
54+
for table in soup.find_all('table'):
55+
parent = table.find_parent('div', class_='table-responsive')
56+
if not parent:
57+
print(f'WARNING: Table missing table-responsive wrapper')
58+
59+
print('Done checking HTML structure.')

0 commit comments

Comments
 (0)