Skip to content

Commit 70c0864

Browse files
added github action to build playlist ever six hours
misc refactoring to build-mfp-index.html.py script added pip requirements.txt file
1 parent 4e2d35e commit 70c0864

4 files changed

Lines changed: 157 additions & 115 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: "Build index.html"
2+
3+
on:
4+
schedule:
5+
- cron: "* */6 * * *"
6+
push:
7+
branches:
8+
- "main"
9+
10+
jobs:
11+
update-index-html:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.13"
18+
cache: "pip"
19+
- name: Install dependencies
20+
run: pip install -r requirements.txt
21+
- name: Run build mfp index.html script
22+
run: python build-mfp-index-html.py
23+
- name: Commit and push
24+
run: |
25+
git diff --quiet index.html
26+
if [ "$?" -eq 1 ];
27+
then
28+
echo "Changes detected in playlist. Publishing..."
29+
git config user.name "github-actions[bot]"
30+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
31+
git add index.html
32+
git commit -m "Update mfp; playlist with latest content."
33+
git push
34+
else
35+
echo "No changes detected in playlist. Skipping publish..."
36+
fi

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@
33
A PWA version of [musicforprogramming.net](https://musicforprogramming.net).
44

55
https://mfp-app.pages.dev/
6-
7-
pip install feeder
8-
pip install requests
9-
pip install Jinja2

build-mfp-index-html.py

Lines changed: 117 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -17,114 +17,120 @@ class Episode:
1717
tracks: str
1818
links: str
1919

20-
episodes = []
21-
22-
23-
#
24-
# always fetch RSS feed to get the latest episodes
25-
# this is the safest way to ensure we have the most up-to-date information
26-
#
27-
28-
feed = feedparser.parse("https://musicforprogramming.net/rss.xml")
29-
30-
print(len(feed.entries))
31-
32-
for entry in feed.entries:
33-
episode = Episode(
34-
title=entry.title,
35-
link=entry.link,
36-
pubDate=entry.published,
37-
guid=entry.guid,
38-
tracks="",
39-
links=""
40-
)
41-
episodes.append(episode)
42-
# print(entry.title)
43-
# print(entry.link)
44-
# print(entry.published)
45-
# print(entry.summary)
46-
# print(entry.guid)
47-
# print()
48-
#print(f"""<a href="{entry.guid}">{entry.title.replace("Episode ", "")}</a>""")
49-
50-
#print(episodes)
51-
52-
#
53-
# fetch the tracklist for each episode from the latest client script
54-
# this is the only way I know of to get the tracklist but it is not guaranteed to work
55-
# in the future if the site changes how it works
56-
#
57-
58-
r = requests.get("https://musicforprogramming.net/latest/")
59-
client_script_start = r.text.find("/client/client")
60-
client_script_end = r.text[client_script_start:].find('";s.type')
61-
#print("client_script_start/end: ", client_script_start, client_script_end)
62-
cur_client_script = r.text[client_script_start:client_script_start + client_script_end]
63-
#print("Current client script:", cur_client_script)
64-
65-
r = requests.get("https://musicforprogramming.net" + cur_client_script)
66-
data = r.text
67-
#print(data)
68-
json_start = data.find("const Xt")
69-
json_end = data.find("function Qt(t)")
70-
#print(json_start, json_end)
71-
json_data = data[json_start:json_end].replace("const Xt=[", "").replace("];", "")
72-
#print(json_data)
73-
74-
json_data_regex = r"\{(.*?)\}"
75-
76-
links_regex = r',"links:.*$'
77-
links_http_regex = r'">(.*?)</a>'
78-
79-
json_objects = [o for o in re.findall(json_data_regex, json_data) if 'type:"episode"' in o]
80-
81-
n = len(json_objects)
82-
83-
for json_obj in json_objects:
84-
scrubbed = json_obj.replace("\",", "\",\"") \
85-
.replace(":\"", "\":\"") \
86-
.replace("{slug", "{\"slug") \
87-
.replace(",\"order:", ",\"order\":") \
88-
.replace(",title\"", ",\"title\"") \
89-
.replace("special:!0", "special\":\"!0") \
90-
.replace("!0,file", "!0\",\"file")
91-
print(scrubbed)
92-
93-
links_removed = re.sub(links_regex, " }", scrubbed)
94-
print(links_removed)
95-
96-
tracklist_start = links_removed.find('"tracklist":"')
97-
tracklist_end = links_removed.find('" }')
98-
print(tracklist_start, tracklist_end)
99-
100-
tracks = links_removed[tracklist_start:tracklist_end] \
101-
.replace('"tracklist":"', '') \
102-
.replace('\\n', '') \
103-
.replace('\\t', '') \
104-
.strip() \
105-
.split('<br>')[:-1]
106-
107-
print("Tracks:", tracks)
108-
109-
link_matches = re.findall(links_http_regex, scrubbed[scrubbed.find('"links:'):])
110-
for link in link_matches:
111-
print("Link:", link)
112-
113-
episode = episodes[n - 1]
114-
episode.tracks = json.dumps(tracks)
115-
episode.links = json.dumps(link_matches)
116-
117-
n -= 1
118-
119-
print("\n\n")
120-
121-
122-
print("Episodes:", episodes)
123-
124-
environment = Environment(loader=FileSystemLoader("templates/"), autoescape=select_autoescape())
125-
template = environment.get_template("mfp-template-index.html")
126-
127-
output = template.render(episodes=episodes)
128-
129-
with open("index.html", "w") as f:
130-
f.write(output)
20+
def main():
21+
episodes = []
22+
23+
24+
#
25+
# always fetch RSS feed to get the latest episodes
26+
# this is the safest way to ensure we have the most up-to-date information
27+
#
28+
29+
feed = feedparser.parse("https://musicforprogramming.net/rss.xml")
30+
31+
print(len(feed.entries))
32+
33+
for entry in feed.entries:
34+
episode = Episode(
35+
title=entry.title,
36+
link=entry.link,
37+
pubDate=entry.published,
38+
guid=entry.guid,
39+
tracks="",
40+
links=""
41+
)
42+
episodes.append(episode)
43+
# print(entry.title)
44+
# print(entry.link)
45+
# print(entry.published)
46+
# print(entry.summary)
47+
# print(entry.guid)
48+
# print()
49+
#print(f"""<a href="{entry.guid}">{entry.title.replace("Episode ", "")}</a>""")
50+
51+
#print(episodes)
52+
53+
#
54+
# fetch the tracklist for each episode from the latest client script
55+
# this is the only way I know of to get the tracklist but it is not guaranteed to work
56+
# in the future if the site changes how it works
57+
#
58+
59+
r = requests.get("https://musicforprogramming.net/latest/")
60+
client_script_start = r.text.find("/client/client")
61+
client_script_end = r.text[client_script_start:].find('";s.type')
62+
#print("client_script_start/end: ", client_script_start, client_script_end)
63+
cur_client_script = r.text[client_script_start:client_script_start + client_script_end]
64+
#print("Current client script:", cur_client_script)
65+
66+
r = requests.get("https://musicforprogramming.net" + cur_client_script)
67+
data = r.text
68+
#print(data)
69+
json_start = data.find("const Xt")
70+
json_end = data.find("function Qt(t)")
71+
#print(json_start, json_end)
72+
json_data = data[json_start:json_end].replace("const Xt=[", "").replace("];", "")
73+
#print(json_data)
74+
75+
json_data_regex = r"\{(.*?)\}"
76+
77+
links_regex = r',"links:.*$'
78+
links_http_regex = r'">(.*?)</a>'
79+
80+
json_objects = [o for o in re.findall(json_data_regex, json_data) if 'type:"episode"' in o]
81+
82+
n = len(json_objects)
83+
84+
for json_obj in json_objects:
85+
scrubbed = json_obj.replace("\",", "\",\"") \
86+
.replace(":\"", "\":\"") \
87+
.replace("{slug", "{\"slug") \
88+
.replace(",\"order:", ",\"order\":") \
89+
.replace(",title\"", ",\"title\"") \
90+
.replace("special:!0", "special\":\"!0") \
91+
.replace("!0,file", "!0\",\"file")
92+
print(scrubbed)
93+
94+
links_removed = re.sub(links_regex, " }", scrubbed)
95+
print(links_removed)
96+
97+
tracklist_start = links_removed.find('"tracklist":"')
98+
tracklist_end = links_removed.find('" }')
99+
print(tracklist_start, tracklist_end)
100+
101+
tracks = links_removed[tracklist_start:tracklist_end] \
102+
.replace('"tracklist":"', '') \
103+
.replace('\\n', '') \
104+
.replace('\\t', '') \
105+
.strip() \
106+
.split('<br>')[:-1]
107+
108+
print("Tracks:", tracks)
109+
110+
link_matches = re.findall(links_http_regex, scrubbed[scrubbed.find('"links:'):])
111+
for link in link_matches:
112+
print("Link:", link)
113+
114+
episode = episodes[n - 1]
115+
episode.tracks = json.dumps(tracks)
116+
episode.links = json.dumps(link_matches)
117+
118+
n -= 1
119+
120+
print("\n\n")
121+
122+
123+
print("Episodes:", episodes)
124+
125+
environment = Environment(loader=FileSystemLoader("templates/"), autoescape=select_autoescape())
126+
template = environment.get_template("mfp-template-index.html")
127+
128+
output = template.render(episodes=episodes)
129+
130+
with open("index.html", "w") as f:
131+
f.write(output)
132+
133+
if __name__ == "__main__":
134+
main()
135+
136+

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# requirements.txt
2+
feedparser
3+
Jinja2
4+
requests

0 commit comments

Comments
 (0)