我的博客

8 月 1 日笔试-Python 版

目录
  1. 第一题
  2. 第二题
  3. 第三题 HTML 模板解析(没做出来)

第一题

小袁报了 N 个课程,每个课程有开始时间和结束时间。但是有一些重复的课程,求小袁最多需要同时听多少门课。

样例:

4
1 4
1 2
2 3
3 4

样例输出: 2

AC 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
n = int(input())
s = []
e = []

for i in range(n):
a, b = map(int, input().split(' '))
s.append(a)
e.append(b)
s.sort()
e.sort()
c = 0
m = 0
ii = 0
jj = 0
while ii < n and jj < n:
if jj != n and (ii == n or e[jj] <= s[ii]):
#print('end', e[jj])
jj += 1
c -= 1
else:
#print('start', s[ii])
ii += 1
c += 1
m = max(m, c)
print(m)

第二题

有 N 个同学,需要给没人分发一张奖券。一个人先拿着所有奖券,留下一张,然后剩余的分个其他人,其他人继续用同样的方法分,直到每人都有一张。

开奖时,每个奖券获得一个整数值(可以是负数),每个人可以选择自己的奖券值作为奖励,也可以额外选择全部或部分他发送出去的奖券,但是如果不选某个奖券,则也无法选择后续的奖券。例如,A 的开始有 1,2,3,4 四张。给了 B 奖券2,3,4,B 又把 3 给了 C, 4 给了 D。则 A 可以选:

1
1,2
1,2,3
1,2,3
1,2,3,4

但不能选如 1, 4

题目分析:

所有人构成一颗树,第一个发的人是树根。使用后序遍历遍历一次就可以得到答案。策略是叶子节点的值只能是自身,其他节点的值是自身加所有非负的孩子。

Python 需要注意的地方:如果书高度很高,会超递归限制,可以模拟递归或者使用 sys.setrecursionlimit 设置递归深度。第二 Python 整数不会溢出,其他语言可能需要考虑递归过程中的溢出问题。

代码:

1
2
3
4
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
import sys
sys.setrecursionlimit(1000000)
mod = 1000000003

n = int(input())
ch = [[] for i in range(n)]
v = [0] * n
root = -1
for i in range(n):
a, b = map(int, input().split(' '))
v[i] = a
if b != 0:
ch[b-2].append(i)
else:
root = i
ans = v[root]
# print(ch)
# print(v)
def vis(i):
global ans
x = v[i]
for child in ch[i]:
r = vis(child)
if r > 0:
x += r
ans = max(x, ans)
return x
vis(root)

print(ans % mod)

第三题 HTML 模板解析(没做出来)

给出一个 JSON 对象,一段 HTML 模板代码,要求返回渲染后的 HTML。

语法类似于 vue:

  1. {{}} 内的值替换为 JSON 对象中的值
  2. y-if 如果为 false 或者 undefined 则删除该 DOM 元素(含子元素)
  3. y-for 列表渲染

样例

{
"isMain": false,
"list": []
}

<div class="head">
<button y-if="{{isMain}}">首页</button>
</div>
<ul class="content">
<!– 卡片区域 –>
<li class="card isMain" y-for="lesson, index in list">
<div class="card-title">
<i y-if="{{lesson.label}}">{{lesson.label.type}}</i>
{{lesson.title}}
</div>
班课<span class="bold">-</span>老师:{{lesson.teacher}}
<div class="lesson-time">{{lesson.time}}<i class="tt"></i></div>
</li>
</ul>
end

这题没时间做了,感觉很麻烦。

力扣周赛 198 - python 解答

目录
  1. 5464 换酒问题
  2. 5465 子树中标签相同的节点数
  3. 5466 最多的不重叠子字符串
  4. 5467 找到最接近目标值的函数值

做出了四道题目,但后两道做的很勉强,也错了几次。刚看第三道题,虽然写着 medium,但是没思路,打开第四道看到反而已经有人过了,于是先做了第四道,才回来做第一道。

最后一道是纯暴力枚举加了几个条件:

  1. 去除相邻重复值
  2. 是函数输出为 0 时停止枚举,因为 0 按位与任何数都依然是 0.
  3. 因为在不断按位与的过程中函数输出是单调减的,所以,但差值大于当前最小差值也可以停止遍历

但是我感觉应该还有更好的解法

第三题用的模拟方法,依次寻找只含一个字母,两个字母 … 的串

5464 换酒问题

https://leetcode-cn.com/contest/weekly-contest-198/problems/water-bottles/

1
2
3
4
5
6
7
8
9
10
class Solution:
def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
c = numBottles
cc = numBottles
kp = numBottles
while kp >= numExchange:
cc = kp // numExchange
kp = kp % numExchange + cc
c += cc
return c

5465 子树中标签相同的节点数

https://leetcode-cn.com/contest/weekly-contest-198/problems/number-of-nodes-in-the-sub-tree-with-the-same-label/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
def countSubTrees(self, n: int, edges: List[List[int]], labels: str) -> List[int]:
ed = {}
for s, e in edges:
if s not in ed:
ed[s] = []
if e not in ed:
ed[e] = []
ed[s].append(e)
ed[e].append(s)
ans = [0] * n
vised = [False] * n
def vis(i):
vised[i] = True
c = {}
c[labels[i]] = 1
if i in ed:
for ch in ed[i]:
if not vised[ch]:
cc = vis(ch)
for k in cc:
if k not in c:
c[k] = 0
c[k] += cc[k]
ans[i] = c[labels[i]]
return c
vis(0)
return ans

5466 最多的不重叠子字符串

https://leetcode-cn.com/contest/weekly-contest-198/problems/maximum-number-of-non-overlapping-substrings/

代码有点长,但是思路还是清晰的。

贪心策略就是:

1. 尽可能选择出现字母数少的子串

2. 在 1 的前提下尽可能选择长度短的

对于出现字母数为 1 的子串,可以特别处理,因为他们一定不会干扰别人,所以可以直接挑出所有仅含一个字母的子串。

第一个循环用于统计每个字母第一次出现位置和最后一次出现位置,顺便还得出了这个字母是否都是连续出现的(如果除了第一次以外,如果有一个该字母前面是其他字母就不是连续出现了)。对每个字母得到三个值 [第一次出现的下标,最后一次出现的下标,是否是连续出现的]

然后定义 used 字典(其实用集合就行)保存有那些字母已经用过了。

ans 就是要返回结果。

nd 是去除了仅含一个字母的子串后的结果,后面会对这个 nd 排序。

循环二,挑出仅含一个字母的子串,这种仅含一个字母的子串自然就是循环一中标记为 True 的子串。

循环三,挑出每个字母构成的子串,并检验是否合法,对每个字母得到: [出现字母数, 长度, 字母, 出现的字母列表]

然后对上面得到的结果降序排序,在一次检验,合法的加入 ans 并在 used 记录。

1
2
3
4
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Solution:
def maxNumOfSubstrings(self, s: str) -> List[str]:
d = {}
for i, ch in enumerate(s): # 循环一,得到每个字母的起点和终点
if ch not in d:
d[ch] = [i, i, True]
else:
if d[ch][1] != i - 1:
d[ch][2] = False
d[ch][1] = i
#print(d)
used = {}
ans = []
nd = []
for ch in d: # 循环二,挑出仅含一个字母的子串
if d[ch][2]:
used[ch] = True
ans.append(s[d[ch][0]:d[ch][1]+1])
for ch in d: # 循环三,挑出每个字母构成的子串,并检验是否合法
if not d[ch][2]:
cc = 0
ccx = set()
f = True

mini = d[ch][0]
maxi = d[ch][1]
change = True
while change:
change = False
i = d[ch][0]
while i <= maxi:
if s[i] in used:
f = False
break
else:
if maxi < d[s[i]][1]:
change = True
maxi = d[s[i]][1]
if mini > d[s[i]][0]:
mini = d[s[i]][0]
change = True
ccx.add(s[i])
i += 1
i = d[ch][0]
while i >= mini:
if s[i] in used:
f = False
break
else:
if maxi < d[s[i]][1]:
change = True
maxi = d[s[i]][1]
if mini > d[s[i]][0]:
mini = d[s[i]][0]
change = True
ccx.add(s[i])
i -= 1
d[ch][1] = maxi
d[ch][0] = mini
if f:
nd.append([len(ccx), maxi-d[ch][0], ch, list(ccx)])
#print(d)
nd.sort()
for n in nd:
f = True
for cc in n[3]:
if cc in used:
f = False
break
if f:
for cc in n[3]:
used[cc] = True
ch = n[2]
ans.append(s[d[ch][0]:d[ch][1]+1])
return ans

5467 找到最接近目标值的函数值

https://leetcode-cn.com/contest/weekly-contest-198/problems/find-a-value-of-a-mysterious-function-closest-to-target/

方法并不太好,但是写起来简单。

两层循环,外层遍历 l 的取值(0 ~ len(arr)),内层遍历 r 的取值(当前 l ~ len(arr))

内层循环是一个不断按位与的过程,所以值一定是不断变小或者不变的,可以利用这个条件提前终止内层循环。

1. 先去除重复的相邻元素

2. 函数输出(ans)为 0 或这最小差(r) 为 0 则退出当前内层循环

3. 最小差为 0 则退出外层循环

4. 如果 target - 当前输出 大于已经得到的最小差,退出内层循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def closestToTarget(self, arr: List[int], target: int) -> int:
arr2 = []
l = arr[0] -1
for v in arr:
if v != l:
arr2.append(v)
l = v
#print(len(arr), len(arr2))
arr = arr2
r = abs(-1000000000 - target)
for i in range(len(arr)):
ans = arr[i]
r = min(r, abs(ans - target))
for j in range(i+1, len(arr)):
ans = ans & arr[j]
r = min(r, abs(ans - target))
if ans == 0 or r == 0:
break
if target > ans and target - ans >= r:
break
if r == 0:
break
return r

C# 函数设置超时

目录

下面代码展示不能单纯使用 System.Threading.Tasks 的 Wait 函数来设置超时,因为只要主进程不退出,子线程扔持续执行。可参考 C# 文档 Wait 函数并不能结束线程,而事实上并没有好办法从线程外部结束线程,除非线程自身支持。

1
2
3
4
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
System.Threading.Tasks.Task t = new System.Threading.Tasks.TaskFactory().StartNew(() => {
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine(i);
Thread.Sleep(1000);
}
}, tokenSource);
t.Wait(3000);
t.Dispose();
for (int i = 0; i < 10; i++)
{
System.Console.WriteLine("hello");
Thread.Sleep(1000);
}
}
}
}

leetcode 785 判断二分图

目录

https://leetcode-cn.com/problems/is-graph-bipartite/

判断一个图是否是二分图

这道题可以用并查集做。

题目给出了每个点相邻的点,我们只需把与一个点相邻的所有点合并,因为与同一个点相邻的所有点一定在二分图的同一侧。

最后再遍历所有点,看每个点和他相邻的点是否在同一集合。如果有在同一集合的情况就不是二分图了。

另外这个题可以用搜索的方法做,给一个点染成红色,再把它所有邻点染为蓝色,并递归下去,如果有冲突则不可行。

1
2
3
4
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
from typing import List
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
fa = [ -1 for i in range(len(graph)) ]
cn = [ 1 for i in range(len(graph)) ]
def getfa(i):
# print(fa, i)
if fa[i] == -1:
return i
# input()
fa[i] = getfa(fa[i])
return fa[i]
def merge(a, b):
# print('merge', a, b)
faa = getfa(a)
fab = getfa(b)
if faa == fab:
return
if cn[faa] > cn[fab]:
faa, fab = fab, faa
fa[faa] = fab
cn[fab] += cn[faa]
# print(fa)
for x in graph:
for n in x[1:]:
merge(x[0], n)
s = set([getfa(i) for i in range(len(graph))])
# print(s)
# print(fa)
for i in range(len(graph)):
fai = getfa(i)
for n in graph[i]:
if fai == getfa(n):
return False
return True

更换域名

目录
  1. 重定向
    1. url 映射代码
    2. 测试
    3. 申请新 https 证书
  2. 博客部署到 blog 目录下
  3. 修改百度统计代码
  4. 域名解析
  5. Valine 评论和阅读统计

这次更换域名改动不少,让我想换域名的直接原因是实习的公司内网屏蔽了所有 .top 域名,而我的博客在百度的收录一直有问题,也怀疑是域名的问题。除了把域名从 codeplot.top 换到 es2q.com 外我还做了如下调整:

  1. 改写所有文章页面 url ,使只包含字母,数字、连字符和下划线
  2. 博客整体移入 blog 文件夹
  3. 部署时也部署到 GitHub Page 的 blog 目录下

因为我在 leetcode 和 SegmentFault 等网站还留了一些我博客的链接,而且在谷歌上还有几百的索引页面,所以需要做原域名的重定向,但是由于更改了很多页面的 url,所以需要做 url 的映射表。然后评论系统和阅读量统计也都需要修复。

重定向

我在新买的华为云服务器上部署了重定向程序,首先通过 sitemap 得到所有文章的 url, 然后手动改写 url,去除了中文和特殊符号等,要同步修改 hexo 的 source 下的 md 文件名。重定向的程序是用 Flask 写的,注意要指明状态码 301(Flask 的 redirect 默认是 302)即永久转移。

用到的 url.txt 文件每行内容是新 path\t原path

url 映射代码

不仅做了映射,还记录请求信息,帮助分析哪里还有未更新的链接

1
2
3
4
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
from flask import Flask, request, redirect
from urllib import parse
import json, time
app = Flask(__name__)

ud = {'index.html': '/blog/index.html', '/': '/blog/'}
f = open('url.txt', encoding='gbk')
for l in f:
nu, ou = l.split('\t')
ud[ou.strip()] = nu
host = 'http://127.0.0.1:4000' # 用于本地测试的域名
host = 'https://es2q.com' # 新域名
f.close()

@app.before_request
def be1():
to_url = host + '/blog' + request.path
Host = request.headers.get('Host')
ua = request.headers.get('User-Agent')
ac = request.headers.get('Accept')
ace = request.headers.get('Accept-Encoding')
acl = request.headers.get('Accept-Language')
try:
ip = request.headers['X-Forwarded-For'].split(',')[0]
except:
ip = request.remote_addr
try:
if request.path in ud:
to_url = host + ud[request.path]
elif parse.quote(request.path) in ud:
to_url = host + ud[parse.quote(request.path)]
elif (request.path+'/') in ud:
to_url = host + ud[request.path+'/']
except Exception as e:
with open('redirect.log', 'a') as f:
f.write(request.path + '\t' + 'err' + '\t' + str(e) + '\n')
with open('redirect.log', 'a') as f:
f.write('%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' %(int(time.time()), ip, Host, ua, ac, ace, acl, request.path, to_url, request.referrer))
return redirect(to_url, code=301)


if __name__ == '__main__':
app.run(port=8001)

测试

测试是否所有链接可以正常访问的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm

ud = {}
f = open('url.txt')
for l in f:
nu, ou = l.split('\t')
ud[ou.strip()] = nu
host = 'http://127.0.0.1:8001'
l = []
for k in ud:
r = requests.get(host + k)
if r.status_code != 200:
print('error', k)
continue
b = BeautifulSoup(r.content.decode(), features='html.parser')
l.append([b.title, k])
#for ll in l:
# print(ll)

还写了一个检查 md 文件中是否有敏感词的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dicts = ['xxx.txt']
wds = []
for d in dicts:
with open(d, encoding='utf8') as f:
for l in f:
wds.append(l.strip())
import os
from tqdm import tqdm
tdir = 'source\\_posts\\'
fs = os.listdir(tdir)
err = []
for fi in tqdm(fs):
with open(os.path.join(tdir, fi), encoding='utf8') as f:
txt = f.read()
for wd in wds:
if wd in txt:
print(fi, wd)
err.append((fi, wd))
for e in err:
print(e)

之前有一次我的百度索引量突然减到 1,我怀疑可能是一篇思政课笔记中有敏感词汇。

申请新 https 证书

原来是 GitHub Pages 给的证书,现在需要一个新的,这个网站:https://freessl.cn/ 非常好用。

博客部署到 blog 目录下

考虑以后整个网站可能有除了博客以外的内容,所以考虑把整个博客放到 https://es2q.com/blog/ 下。可以通过修改 _config.yml:

1
2
3
4
5
6
7
# URL
url: https://es2q.com/blog/
root: /blog/

# Directory
source_dir: source
public_dir: public/blog/

但是这样对 hexo-deployer-git 并不有效。

因为它的代码中直接复制了 public_dir 的内容,然后 push 到 github 上。

我是通过修改 hexo-deployer-git 的代码实现了 push 到 blog 目录下,需要修改的文件是:\node_modules\hexo-deployer-git\lib\deployer.js

首先修改代码开始的 publicDir,直接写成 ‘public’。

1
var publicDir = 'public';//this.public_dir;

第二找到这一句 return fs.copyDir(publicDir, deployDir, opts);

在下面添加:

1
2
3
4
5
6
7
8
9
10
11
  // 前面的内容这里省略了
return fs.copyDir(publicDir, deployDir, opts);
}).then(function() {
return fs.copyFile(publicDir+'/blog/CNAME', deployDir+'/CNAME'); // 复制 CNAME,Github Pages 绑定域名用的
}).then(function() {
return fs.copyFile(publicDir+'/blog/index.html', deployDir+'/index.html'); // 把 index 复制到根目录来一份
}).then(function() {
return fs.copyFile(publicDir+'/blog/sitemap.xml', deployDir+'/sitemap.xml'); // 把 sitemap 复制到根目录一份,如果没有生成 sitemap就不需要这个
}).then(function() {
return fs.copyFile(publicDir+'/blog/baidusitemap.xml', deployDir+'/baidusitemap.xml'); // baidu sitemap,同上一条
}) // 后面还有内容,是原来的代码

修改百度统计代码

更换了域名所以要重新生成百度统计代码。可以放到主题文件的 header 或者 footer 里。

域名解析

把新域名解析到 GitHub Pages,同时调整 GitHub 仓库配置,然后把旧域名解析到运行重定向服务的服务器上,并配置好 Nginx。

Valine 评论和阅读统计

阅读统计 id 前面要添加 /blog/。评论则需要修改后台数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%- partial('_partial/article', {post: page, index: false}) %>
<script src='//unpkg.com/valine/dist/Valine.min.js'></script>
<!-- id 将作为查询条件 -->
<span id="/blog/<%= page.path %>" class="leancloud_visitors" data-flag-title="<%= page.title %>">
<em class="post-meta-item-text">阅读量 </em>
<i class="leancloud-visitors-count"></i>
</span>
<p>评论无需登录,可以匿名,欢迎评论!</p>
<div id="vcomments"></div>
<script>
new Valine({
el: '#vcomments',
appId: 'vdTqlvkiIk2AmomVRXrR9kb0-gzGzoHsz',
appKey: 'yrlSqvbHUJqqVzH2rb0t2X9B',
visitor: true
})
</script>

Tactics Core 小游戏(炎龙骑士团)

目录
  1. 可行的策略
  2. 隐藏元素

大概是上小学的时候玩过这个游戏,当时是在“爱威海”网站的小游戏频道里找到的这个游戏(当时一个同学告诉我这个网站的,那时候家里也是刚买了电脑,喜欢找各种有趣的网站看)。当时记得周五放学回家就打开电脑,看看爱威海上的小游戏有没有更新。现在爱威海已经没有小游戏频道了。当时就特别喜欢这个游戏,操作简单,看不懂游戏里的英语也学会了。开始打不过电脑,感觉电脑的人物都好厉害,Mystical Sorceress(对方的老大)可以同时攻击多个不相邻的人(我方老大只能攻击一个十字上的人,可能误伤自己人),我方医生只能治疗相邻的人,对方医生却可以治疗远处的人。但是实际上我方的角色看起都比对方弱一点,但是我方比对方多一人(电脑 6 个人,我方有 7 个人)。

游戏的开发商是 Digital Deed Entertainment,游戏开始页面上的简介中说,这个游戏是一个用于演示Tactics Core 引擎的例子(这个引擎大概就是他们公司的产品)。他们官网上也有几个他们开发的其他游戏。

他们的官网上也有这个游戏的简介:http://www.digisonline.com/tacticscore/ , 也提供了在线试玩的链接,但是在我的浏览器里不工作,4399上也有这个游戏,4399上这个游戏名称被翻译为炎龙骑士团(链接:http://www.4399.com/flash/37906_3.htm)。

我的视频中展示了一种策略,可以保证全部角色存活,并且有两个角色未受到伤害(医生和老大)。视频链接:https://www.bilibili.com/video/BV1vD4y1Q7Pb/

可行的策略

其实我方本身人数比对方多,铁剑哨兵(Ironclad Sentinal)血量和防御都高,不太容易死(但是其他角色都比较脆)。

如果想所有角色都存活有如下策略:

  1. 开始先集中火力打死或打残一个人,我一般选择敌方红色剑客(Macabre Swordsman)或者蓝色剑客(Talon Soldier)如我的视频里,是先打红色剑客,如果运气好,一轮攻击就能打死,运气不好(如我视频中的情况)一轮后,加上我方刺客一次攻击也能打死了。
  2. 然后打死对方的老大。
  3. 可以用移动速度快的角色把敌方射手逼入地图上方的角落,只要他进去了一般就只有一次攻击机会,因为你紧挨着他时他无法攻击。
  4. 如果想减少我方角色死亡应该避免我方角色被集中火力,要让伤害分布均匀。

隐藏元素

如果我方除了医生(Mystical Healer)以外所有角色都死亡了,那么医生会变成Daemon,血量是 ????,但是实际血量是有限的,感觉和普通角色差距不大,由于她速度慢,如果敌人太多还是会被打死。

image.png

image.png

她一次能走6步

image.png

攻击范围是4,每次攻击一个格子。她每次攻击造成的伤害都是 9999,一次可以秒杀任意一个范围内的敌人。

image.png

MacOS上使用Selenium控制Safari

目录

苹果官方文档:

https://developer.apple.com/documentation/webkit/about_webdriver_for_safari

https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari

  1. 首先要确认 safaridriver 已经安装,路径在 /usr/bin/safaridriver

  2. 然后修改 Safari 配置允许自动控制

    image.png

    先在配置中显示 Develop 菜单,然后到 Develop 菜单中开启运行远程自动控制

    image.png

  3. 安装 selenium

    pip3 install selenium

  4. 使用 selenium 启动浏览器

    1
    2
    3
    4
    from selenium import webdriver

    driver = webdriver.Safari()
    driver.get('https://codeplot.top/')

在Windows10上安装MacOS虚拟机(VirtualBox + AMD ryzen)

目录
  1. 安装依赖
  2. 开始安装

试了很多方法都失败了,最后找到了 https://github.com/myspaghetti/macos-virtualbox/

尝试使用 cywin 但是卡在这里了:

1
2
3
Please make sure the following packages are installed:
coreutils gzip unzip xxd wget
Please make sure the coreutils and gzip packages are the GNU variant.

然后尝试WSL,结果很好用

安装依赖

  1. unzip

    sudo apt install unzip

  2. dmg2image

    1
    2
    3
    sudo add-apt-repository universe
    sudo apt update
    sudo apt install dmg2img
  3. Oracle VM VirtualBox Extension Pack

    也是 virtualbox 下载页面下载https://www.virtualbox.org/wiki/Downloads

开始安装

git clone https://github.com/myspaghetti/macos-virtualbox.git

进入该目录后执行

1
2
chmod +x macos-guest-virtualbox.sh
./macos-guest-virtualbox.sh

可能是因为 AMD CPU 的问题,开机卡住了,最后一行是 EXITBS 之类的。

然后参考https://github.com/myspaghetti/macos-virtualbox/issues/8 解决,关闭虚拟机,到 virtual box 的安装目录执行 VBoxManage modifyvm "macOS" --cpu-profile "Intel Core i7-2635QM" 再次开机就可以了。

搭建局域网视频直播服务:使用 node media server

目录
  1. 服务器配置
  2. 直播端配置
  3. 客户端

推流软件(直播视频采集):OBS Studio,一款免费的开源软件,支持 Window,Linux 和 MacOS

直播服务器:nodejs 的 node media server(nms)

Web 端视频播放器:flv.js

直播端和服务器可以是同一台机器。

服务器配置

  1. 安装 nodejs

    访问官方网站: https://nodejs.org/en/

    下载安装包并安装,有 LTS 和 Current 都可以,一般选 LTS

    image.png

  2. 进入目录 nms 执行 install.bat

  3. 执行 run.cmd

  4. 执行 run2.cmd

    此时会显示网址,就是在局域网内可以访问的地址,等配置好直播端,直接访问这个地址就可以观看直播了。

    image.png

直播端配置

  1. 安装 OBS Studio

  2. 配置

    image.png

    点击 sources 的加号,选择 Display Capture,这个会捕获整个屏幕的视频。再点击确定。

image.png

然后点击右侧的 Start Streaming 开始推送,但是第一次使用没有配置服务器,会弹出提示,点击打开配置即可

image.png

打开配置后点击左侧第二个 Stream,右边的第一个 Service 选择 Custom(即自定义),Server 填 rtmp://127.0.0.1/live, Stream Key 填 test,然后点 OK。

配置好再次点击 Start Streaming

客户端

最后在同一个局域网的机器上打开上面的网址就可以观看直播了

打卡助手使用说明

目录

这是我业余开发的项目,主要是帮助大家整理乐心运动步数打卡的图片,可以自动识别出步数和日期,并整理出时间线。可以自定义提醒时间,如果到了提醒时间还没有完成打开会自动推送提醒(这个还没有开发完成)。

效果是:

image.png

使用方法:

  1. 关注我的公众号:Colander漏勺

    qrcode_for_gh_71c26a4b3cca_258.jpg

  2. 点击公众号右下角菜单漏勺首页进入首页并注册和登录

  3. (这步可以跳过,不绑定微信也可以使用多数功能,但是无法收到推送提醒)绑定微信,登录后选择微信管理,点击绑定微信,然后才能收到推送提醒

  4. 给公众号发送打卡图片,需要点击公众号底部的切换按钮切到发送消息模式,然后点击加号,发送乐心步数页面打卡图片。