分类: 编程相关

  • 小程序图片展示尺寸优化

    小程序图片展示尺寸优化

    近期制作了一个类似论坛/朋友圈/树洞的社交类小程序(未公开使用)

    沙雕网友们的沙雕图是大伙的快乐源泉,所以图片的展示就极为重要

    这里只讨论类似微信朋友圈的图片尺寸方面的思路

    大致思路

    大致需要考虑一条动态不能占用过大的尺寸以至于不能让同屏展示的动态数过少

    然后要考虑图片展示的美观性,在上面那个前提下,尽可能平衡图片细节和图片的大小

    而且不能让任何一张图片被拉伸,加上mode="aspectFill"属性即可


    一条有图片的动态可分为单张图和多张图

    在多张图(每行三张)的情况下,我们将全部横向空间分为三张图片和两个间隔,以下内容将主要以该尺寸模式延伸

    在这里我们以两张图片和一个间隔为基准,我们在暂时假设一个尺寸:图片宽度64px,间隔8px

    多张图片

    多张图片所有图片使用正方形展示

    按上文所述 将全部横向空间分为三张图片和两个间隔 计算出每张图片的尺寸

    图片数量234
    排列模式1×21×32×2

    按照上表的模式(4张图片除外),更多图片使用多行处理即可

    展示效果如下图

    单张图片

    获得图片的尺寸信息

    微信小程序可以通过wx.getImageInfo()获得图片的尺寸信息

    图片的简单分类

    根据图片的长宽比,大致可以分为两类分别是宽图片(宽大于高)高图片(高大于宽)

    根据两种分类就有了两个不同的优化方式,但两者十分类似

    宽图片

    宽图片在处理上较为简单,最大宽度可以占用全部横向空间

    第一步 设置宽度

    我们将图片的宽度设置为一个基准(以两张图片和一个间隔为基准,后面不再赘述)

    然后,高度根据图片的原始比例计算(由于是宽度图片,高度不会超过基准)

    第二步 确定高度

    在第一步后,我们很容易发现问题,如果一张图片类似这样:

    这样十分不直观,所以我们需要确定一步高度,更准确一点就是确定最小高度

    为了方便,我们取黄金比例0.618

    就是在正常情况下图片高度太小,我们就引入黄金比例(非将图片以黄金比例展示,仅使用0.618)

    我们这里使用上面的多张图展示的图片边长64px × 1.618为最小高度

    取高度

    如果根据第一步设置宽度后,按照图片原始比例计算高度大于最小高度,则取图片原始比例计算高度

    反之:按照上面的多张图展示的图片边长64px × 1.618为高度

    也就是根据图片原始尺寸比例的计算高度与最小高度 两者取较大值

    第三步 重新确定宽度

    在上面两步后,图片的展示已经足够完善了,但是仍有一个问题,如果一张图片按照正常情况,高度接近最小高度(由于是正常情况,故高度比最小高度大)

    另一张图使用最小高度

    两张图实际的长宽比相差可能会很大,但展示效果却十分相似,所以我们需要对两种情况进行区分

    上方提到过,图片最大宽度可以占用全部横向空间

    所以在使用了最小高度时,我们将图片的宽度铺满全部空间

    结果

    在以上处理后,实测图片没有出现图片过大等问题

    高图片

    高图片与宽图片类似

    但我们无法将高图片的高度设置过高,太高就会单条动态占用过大空间

    第一步 设置高度

    我们首先将高度设置为一个基准

    然后,宽度根据图片的原始比例计算

    第二步 确定宽度

    类似宽图片,如果图片“过细”,效果也不太好

    同样我们设置一个最小宽度与最小高度一致64px × 1.618

    根据图片原始尺寸比例的计算宽度与最小宽度 两者取较大值

    结果

    高图片与宽图片不同,高图片在使用最小宽度后没有拉长高度,这是因为继续拉长会过高显得右边空白过大

    理后,实测图片没有出现图片过大等问题

  • 微信小程序云开发数据库实时收据推送

    最近接到一个项目,只有两三天的开发时间,就是学校音乐协会专场的现场互动抽奖小程序

    由于时间有限,只做了节目热度节目切换热度排行榜现场抽奖

    最核心的东西就是小程序云开发数据库的实时推送

    开放文档

    根据文档,嗲用Collection上的watch方法即可坚挺给定查询条件对应的数据,可以使用orderBylimit

    也就是说使用watch方法可以被动接收数据库的变化

    具体实现

    我这里放出文档中体现较完整的中奖列表的实时刷新

    prizeBoardWatcher = db.collection('prize')
          .orderBy('time', 'desc')
          .watch({
            onChange: function (snapshot) {
              console.log(snapshot.docs);
              that.setData({
                prize: snapshot.docs
              })
            },
            onError: function (err) {
              console.error('the watch closed because of error', err)
            }
          })

    代码很简单能实现想要的效果

    问题

    具体我遇到了一个比较严重的问题,就是有时候会监听失败

    the watch closed because of error Error: errCode: -402002 realtime listener init watch fail | errMsg: login fail Error: wsclient.send timedout
    history states: UNINIT-(loginStart)->INIT_LOGGING_IN-(loginFail)->INIT_LOGIN_FAIL-(loginStart)->INIT_LOGGING_IN-(loginFail)->INIT_LOGIN_FAIL-(loginStart)->INIT_LOGGING_IN-(loginFail)->INIT_LOGIN_FAIL-(close)->CLOSED

    这个问题我也搜了一下,发现很多人都会遇到,但是没有给出解决方案,但是根据官方案例似乎没有遇到这样的问题

    所以很可能是自己代码性能优化的问题

    这里有一篇文章如何用云开发打造万人同屏高并发实时互动小程序

    在专场结束后虽然需求方很满意,但是我还是第一时间打开了微信开发者工具看看到底是哪里出了问题

    最后也只能找到几个可以优化的地方

    优化

    首先是开始监听的位置,根据官方文档给出的例子,const watcher = db.coll......,我是直接将其放在了生命周期函数中的onLoad

    重新看了一遍小程序生命周期的文档

    发现显然在onShow中开始监听,在onHide中暂停监听更好

    希望在下次现场活动中能够验证本次优化,我也会在下次充分准备希望能解决或降低问题出现的概率

  • 使用小程序云开发写爬虫的一个坑

    如果目标文档是GBK编码的,那么请求头里面一定要去掉这行'Accept-Encoding': 'gzip, deflate',否则神仙都救不回来

  • 天翼云盘下载解析工具

    天翼云盘下载解析工具

    2020-12-21:程序已失效。本程序已删除并不再更新
    2020-12-16:多文件解析已修复
    2020-12-15:程序已失效

    最近发现很多资源都是用天翼云盘发出来的,闲来无事做了一个解析工具,以后就不用登录(找回密码)再下载了。

  • WordPress评论/回复邮件提醒

    代码转载自互联网,本人在发送邮件样式上有稍作修改

    在主题的function.php中添加如下代码

    /* comment_mail_notify v1.0 by willin kan. (所有回复都发邮件) */
    function comment_mail_notify($comment_id) {
      $comment = get_comment($comment_id);
      $parent_id = $comment->comment_parent ? $comment->comment_parent : '';
      $spam_confirmed = $comment->comment_approved;
      if (($parent_id != '') && ($spam_confirmed != 'spam')) {
        $wp_email = 'no-reply@' . preg_replace('#^www.#', '', strtolower($_SERVER['SERVER_NAME'])); //e-mail 发出点, no-reply 可改为可用的 e-mail.
        $to = trim(get_comment($parent_id)->comment_author_email);
        $subject = '您在 [' . get_option("blogname") . '] 的留言有了回复';
        $message='    <div style="box-shadow: 0px 0px 10px 5px rgba(85, 85, 85, 0.10);border-radius: 10px;padding:20px;margin: 10px;">
        <div style="font-size: larger;">
            您在 ['.get_option ("blogname").'] 的留言有了新的回复
        </div>
        <div style="margin-left: 5px;margin-right: 5px;">
            <p>您曾在<span style="color:#23b7e5;font-weight:bold">《'.get_the_title ($comment->comment_post_ID ).'》</span>的留言:
            </p>
            <div style="margin:10px;border:1px solid #dfdfdf;padding:8px;color:#a0a0a0;border-radius: 5px;">
                '.trim(get_comment
                ($parent_id)->comment_content ).'</div>
            <p><span style="color:#23b7e5 ;font-weight:bold">'.trim($comment->comment_author ).' </span>给您的回复:</p>
            <div style="margin:10px;border:1px solid #dfdfdf;padding:8px;color:#a0a0a0;border-radius: 5px;">
                '.trim($comment->comment_content
                ).'
            </div>
            <p>您可以点此 <a href="'.get_permalink ($comment->comment_post_ID ).'"
                style="text-decoration:none;color:#5d9cec;">查看回复的完整內容</a></p>
        </div>
        <div style="margin-top:20px;color:rgb(53, 53, 53);font-size:12px;">请注意:此邮件由[<span
            style="color:#5d9cec"> '.get_option ("blogname").'</span> ]自动发送,请勿回复</div>
    </div>';
          $from = "From: \"" . get_option('blogname') . "\" <$wp_email>";
          $headers = "$from\nContent-Type: text/html; charset=" . get_option('blog_charset') . "\n";
          wp_mail( $to, $subject, $message, $headers );
      }
    }
    add_action('comment_post', 'comment_mail_notify');
  • 「微信小程序踩坑记1」自定义tabbar

    「微信小程序踩坑记1」自定义tabbar

    写在前面

    我在上学期就开始做一款学校用的小程序,就是课表那些,无奈如上一篇文章所说的代码丢失,那个小程序的源码就正正好好丢失了,半个月的成果,好不容易涨到每天超过1000人使用,因为代码丢失没法更新换学期,于是截至3月19日每天最高不到150人访问。

    当然,小程序是重写了的,这次就没有像之前那样,借鉴另外一款小程序的其中一个UI组件,然后自己设计UI,这次使用了Vant Weapp,这个用的是微信小程序自定义组件,所以与之相关的坑就接踵而至,今天先说一下这个tabber

    Vant Weapp tabber的使用

    根据文档,这个tabber图标的切换用的是一个acitve变量,所以我们可以单独放一个页面,最底部50px用作tabber的位置,然后上面的部分用来放其子页面。

    有了这么一个思路,我们就将子页面都按照自定义组件来用,把当前active的页面显示出来,其他的隐藏。

    具体操作

    我们假设页面的容器和tabber单独的页面放在根目录,并命名为index,所以我们在index.json通过如下代码引入

    "usingComponents": {
      "van-tabbar": "path/to/@vant/weapp/dist/tabbar/index",
      "van-tabbar-item": "path/to/@vant/weapp/dist/tabbar-item/index"
    }

    然后就是tabber的框架,我们预留了50px,并且置于最顶层(如果有遮罩层之类的可能需要另外考虑层级)

    <view style="height:50px">
      <van-tabbar active="{{ activeIndex }}" z-index="2" bind:change="onChange">
        <van-tabbar-item icon="home-o">首页</van-tabbar-item>
        <van-tabbar-item icon="tosend">课程表</van-tabbar-item>
        <van-tabbar-item icon="orders-o">公告</van-tabbar-item>
        <van-tabbar-item icon="setting-o">设置</van-tabbar-item>
      </van-tabbar>
    </view>

    然后就是子页面的容器了

    <view class='wrapper' style='margin-bottom:50px;'>
      <page0 wx:if='{{activeIndex == 0}}' bind:topage="topage"></page0>
      <page1 wx:if='{{activeIndex == 1}}'></page1>
      <page2 wx:if='{{activeIndex == 2}}'></page2>
      <page3 wx:if='{{activeIndex == 3}}'></page3>
    </view>

    上面的page0page1就是子页面的component-tag-name了,也需要在index.json里面声明,注意:这里的usingComponents需要和上面的合并

    "usingComponents": {
        "page0": "../pages/index/index",
        "page1": "../pages/classtable/classtable",
        "page2": "../pages/notice/notice",
        "page3": "../pages/setting/setting"
      }

    最后就是在tabber上的bind:change和类似于page0上的bind:topage="topage"

    bind:change是用于切换页面的,容易理解;bind:topage是为了在子页面来切换页面,也算一个坑,这里也说一下

    onChange(event) {
        // event.detail 的值为当前选中项的索引
        if (event.detail == this.data.activeIndex) return;
        this.setData({
          activeIndex: event.detail
        })
      }
    topage(event) {
        // console.log(e.detail)
        this.onChange(event)
      }

    那个在子页面切换页面的在子页面中如下实现

    this.triggerEvent("topage", 1)

    最后

    感觉微信小程序就是个无底洞,各种坑,自定义组件、只能异步请求等等。

    我这个解决方法只是其中一种,并不是最好的解决方法。

  • C++多项式相加 & 括号匹配

    C++多项式相加 & 括号匹配

    写在前面

    换了个主题

    今天抽了点空出来换了个主题,感觉还行吧就不单独写个文章了。

    然后太久没写博客了,Hexo是怎么用的都快忘干净了。

    正文

    情况就随便提两句完事,尽快进入正题吧。

    这次的题目来源于C++的大作业,正好我在网上没有找到合适的代码,就自己写了一个,也将其分享出来。

    这里主要弄的是数据结构,可能里面会有一些小问题,由于是一个很正常的作业,就没有细究了。

    多项式相加

    多项式相加我只写了链表的,没写顺序表的。

    设有多项式A (x)和B (x),
    求C (x)= A (x)+B (x)

    运算规则:将两个多项式中指数相同的项对应系数相加,若和不为零,则构成C (x)中的一项; A (x)和B (x)中所有指数不相同的项均复抄到C (x)中。

    #include"iostream"
    #include"stdlib.h"
    using namespace std;
    
    typedef int ElemType;
    
    typedef struct node {
        ElemType a;//系数
        ElemType b;//指数
        struct node *next;
    }NodeType;
    
    class LinkList {
    public:
        NodeType *Head;
        LinkList();
        ~LinkList();
        void Creat();
        void Print();
        int GetLength();
        int Locate(ElemType x);
        NodeType * GetElem(int i);
        void Insert(int i, ElemType a, ElemType b);
        void Delete(int i);
    };
    
    LinkList::LinkList() {
        Head = new NodeType;
        Head->next = 0;
    }
    
    int LinkList::GetLength() {
        int num = 0;
        NodeType *p;
        p = Head->next;
        while (p != NULL) {
            num++;
            p = p->next;
        }
        return(num);
    }
    
    void LinkList::Print() {
        NodeType *p;
        p = Head->next;
        cout << "D=";
        while (p != NULL) {
            cout << p->a << "X^" << p->b;
            if (p->next != NULL) {
                cout << " + ";
            }
            p = p->next;
        }
    }
    
    NodeType * LinkList::GetElem(int i) {
        NodeType *p = Head->next;
        int pos = 1;
        if (i<1 || i>GetLength()) {
            cout << "error" << endl;
            return 0;
        }
        while (pos < i) {
            p = p->next;
            pos++;
        }
        return p;
    }
    
    int LinkList::Locate(ElemType x) {
        NodeType *p;
        int loc = 1;
        p = Head->next;
        while (p != NULL && p->b != x) {
            p = p->next;
            loc++;
        }
        if (p != NULL) {
            return loc;
        }
        else {
            return -1;
        }
    }
    
    void LinkList::Insert(int i, ElemType a, ElemType b) {
        NodeType *p, *s;
        if (i<1 || i>GetLength() + 1) {
            return;
        }
        if (i == 1) {
            p = Head;
        }
        else {
            p = GetElem(i - 1);
        }
        s = new NodeType;
        s->a = a;
        s->b = b;
        s->next = p->next;
        p->next = s;
    }
    
    void LinkList::Delete(int i) {
        NodeType *p, *t;
        if (i<1 || i>GetLength()) {
            cout << "error" << endl;
            return;
        }
        if (i == 1) {
            p = Head;
        }
        else {
            p = GetElem(i - 1);
        }
        t = p->next;
        p->next = t->next;
        delete t;
    }
    
    void LinkList::Creat() {
        int i = 1;
        NodeType *p, *s;
    
        p = Head;
        cout << "请输入系数与指数(空格隔开)" << endl;
        while (1) {
            s = new NodeType;
            cout << "第" << i << "个:";
            cin >> s->a;
            if (s->a == -1) {
                break;
            }
            cin >> s->b;
            s->next = NULL;
            p->next = s;
            p = s;
            i++;
        }
    }
    
    LinkList:: ~LinkList()
    {
        NodeType *p, *q;
        if (Head->next != 0)
        {
            q = Head->next;
            while (q != NULL)
            {
                p = q;
                q = q->next;
                delete p;
            }
        }
        delete Head;
    }
    
    void main()
    {
        LinkList f;
        LinkList s;
        LinkList r;
    
        NodeType *p;
    
        cout << "请输入第一个多项式:" << endl;
        r.Creat();
        cout << endl;
    
        cout << "第一个多项式:";
        r.Print();
        cout << endl;
    
        cout << "请输入第二个多项式:";
        s.Creat();
        cout << endl;
    
        cout << "第二个多项式:";
        s.Print();
        cout << endl;
    
        p = f.Head->next;
    
        while (p != NULL) {
            int b = p->b;
            int a = p->a;
            int po = s.Locate(b);
            if (po != -1) {
                a += s.GetElem(po)->a;
                s.Delete(po);
            }
            r.Insert(r.GetLength() + 1, a, b);
            p = p->next;
        }
    
        p = s.Head->next;
        while (p != NULL) {
            int b = p->b;
            int a = p->a;
            r.Insert(r.GetLength() + 1, a, b);
            p = p->next;
        }
        cout << endl;
        cout << "相加结果:";
        cout << endl;
        r.Print();
    
        system("pause");
    }

    括号匹配

    算术表达式中,可以包含圆括号()、方括号[] 。括号使用规则:出现左括号,必有相应的右括号与之匹配,每对括号之间可以嵌套,但不能出现交叉情况。

    设计算法,检验算术表达式中括号的合法性。

    方法:使用一个栈保存每个出现的左括号,扫描到右括号时,从栈中弹出左括号,检验匹配情况。遇到以下情况之一,说明括号不匹配:

    扫描到一个右括号时栈已空,说明目前右括号多于左括号

    栈中弹出的左括号与当前扫描到的右括号类型不同,说明括号交叉

    算术表达式扫描完毕,栈中还有没有匹配的左括号,说明左括号多于右括号

    顺序栈的数据结构(类及其成员函数的设计)

    typedef char ElemType;
    const int MAXSIZE = 100;
    
    class SqStack {
    private:
        ElemType elem[MAXSIZE];
        int top;
    public:
        SqStack() {
            top = 0;
        }
        ~SqStack() {};
        int IsEmpty();
        void Push(ElemType e);
        ElemType Pop();
        //void Print();
        ElemType GetPop() {
            return elem[top];
        }
    };
    
    
    void SqStack::Push(ElemType e) {
        if (top == MAXSIZE - 1) {
            cout << "栈上溢错误" << endl;
            return;
        }
        top++;
        elem[top] = e;
    }
    
    ElemType SqStack::Pop() {
        ElemType x;
        x = elem[top];
        top--;
    
        return x;
    }
    
    int SqStack::IsEmpty() {
        if (top == 0) {
            return 1;
        }
        else {
            return 0;
        }
    }

    链栈的数据结构(类及其成员函数的设计)

    typedef char ElemType;
    typedef struct node {
    	ElemType	data;
    	struct node	*next;
    }NodeType;
    
    class LinkStack
    {
    public:
    	NodeType *top;
    	LinkStack()
    	{
    		top = NULL;
    	}
    
    	~LinkStack()
    	{
    		while (!IsEmpty())
    			Pop();
    	}
    
    	int IsEmpty()
    	{
    		if (top == NULL)
    			return(1);
    		else return(0);
    	}
    
    	void Push(ElemType e)
    	{
    		NodeType *p;
    		p = new NodeType;  p->data = e;  p->next = top;  top = p;
    	}
    
    	ElemType Pop()
    	{
    		NodeType *p;   ElemType x;
    		x = top->data;  p = top;  top = top->next; delete p;
    		return(x);
    	}
    
    	ElemType GetTop()
    	{
    		return(top->data);
    	}
    };

    括号匹配函数的设计

    int Matching()
    {
    	LinkStack s;
    
    	cout << "请输入符号(回车键结束):";
    	char ch = getchar();
    	while (ch != '\n')
    	{
    		if (ch == '(')
    		{
    			s.Push(ch);
    		}
    		else if (ch == '[')
    		{
    			s.Push(ch);
    		}
    		else if (ch == '{')
    		{
    			s.Push(ch);
    		}
    		else if (ch == ')')
    		{
    			if (!s.IsEmpty())
    			{
    				if (s.Pop() != '(')
    				{
    					return 0;
    				}
    			}
    		}
    		else if (ch == ']')
    		{
    			if (!s.IsEmpty())
    			{
    				if (s.Pop() != '[')
    				{
    					return 0;
    				}
    			}
    		}
    		else if (ch == '}')
    		{
    			if (!s.IsEmpty())
    			{
    				if (s.Pop() != '{')
    				{
    					return 0;
    				}
    			}
    		}
    		ch = getchar();
    	}
    
    	if (s.IsEmpty())
    	{
    		return(1);
    	}
    	else {
    		return(0);
    	}
    }

    程序主函数的设计

    void main()
    {
    int n = Matching();
    if (n)
    {
    cout << "匹配成功!";
    }
    else {
    cout << "匹配失败!";
    }

    system("pause");
    }

    最后

    这里就不赘述顺序存储和链式存储的区别和对比了,我也不是主要学C++的,学校开设的大学计算机基础课程就要求学这玩意,希望有和我同样需求的人能够通过这篇用到和课本知识匹配的恰到好处的文章来简化作业。

  • 关于宿舍洗衣机的折腾

    好久没管这个小破网站了,因为实在是太忙也没有什么有意思的东西可以写,刚好无聊折腾一下宿舍楼下的洗衣机


    写在前面

    原因

    宿舍楼下的洗衣机原本是有预约功能的,可能是男生宿舍的洗衣机少(一整栋楼7台),然后就把预约关了,从此只能下到一楼(我住5楼)碰运气看看有没有空的洗衣机。

    描述

    我们宿舍的洗衣机就是级身上有一个二维码,通过APP和二维码付款使用,APP名称叫U净,如果有相同的可以试下。不用多说,方法肯定是抓包。

    解决方法 1.0

    Alt text

    这个方法确实好用,是上个学期我已经在用的,那么就是把洗衣机上的二维码全部拍下来放到宿舍,就能远程预约了。我的大概长这样:

    但是使用中还是有一些问题:

    • 喵喵机打印用的是热敏纸,会掉色
    • 作为一个懒人,睡上铺的怎么会下床做这种事呢

    所以我是不可能就这样罢休的:

    解决方法 2.0(正在使用)

    话不多说,上软件,抓包:

    Alt text
    Alt text

    不难找到,我们需要的就是这一个:

    找一个模拟HTTP请求

    填上地址和header,就得到了下面这玩意:

    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
    {
    “errorCode”: 0,
    “result”: {
    “no”: “63”,
    “sn”: “0000DB11138108443188111110040000”,
    “qrCode”: “http://app.littleswan.com/u_download.html?type=Ujing&uuid=0000000000000A0007604201806230011246”,
    “deviceModel”: “0000.db.27974”,
    “alipayOnly”: 2,
    “registerType”: 0,
    “brand”: 1,
    “readableName”: “校园机MG80-GF03DW 一代”,
    “status”: “1”,
    “online”: true,
    “statusAll”: “{\”switch\”:1,\”wash\”:1,\”wash_mode\”:1,\”time_remaining\”:14,\”fault_warn\”:\”0\”}”,
    “remainTime”: 14,
    “statusUpdateTime”: “2019-09-29T13:15:52.000Z”,
    “virtualId”: “53876069890835”,
    “errorCode”: “E30”,
    “scanSelfClean”: null,
    “errorTime”: “2019-09-24T15:00:15.000Z”,
    “endWorkTime”: null,
    “shutDownTime”: null,
    “offlineTime”: “2019-09-17T10:28:13.000Z”,
    “addTime”: “2018-10-04T09:30:22.000Z”,
    “isRemoved”: false,
    “hasAutoLaunchDevice”: false,
    “autoLaunchDeviceOutOfStock”: null,
    “washTemperatureIsEnable”: false,
    “isSlotMachine”: false,
    “moduleType”: 0,
    “isForceDelete”: null,
    “usedCounts”: null,
    “isBindToAlipay”: true,
    “bindToAlipayTime”: “2018-10-15T09:19:47.000Z”,
    “washCounts”: 0,
    “washCoins”: 0,
    “bleVersion”: null,
    “firstBindTime”: “2018-10-04T09:30:26.000Z”,
    “nbSignal”: null,
    “macAddress”: null,
    “deviceId2G”: null,
    “bleBindStatus”: null,
    “runTimes”: 31,
    “deviceTypeId”: 2,
    “errorCodeId”: null,
    “franchiseeId”: 355,
    “applyFranchiseeId”: null,
    “orderId”: 142085187,
    “serviceSubjectId”: 8880,
    “storeId”: “5bb0c9e85ae9d5634c00000e”,
    “boxTypeId”: null,
    “store”: {
    “id”: “5bb0c9e85ae9d5634c00000e”,
    “mobile”: “4006185141”,
    “enable”: true,
    “isRemoved”: false
    },
    “currentOrder”: {
    “status”: “40”,
    “statusUpdateTime”: “2019-09-29T14:01:47.000Z”,
    “remainTime”: 840,
    “id”: 142085187
    }
    }
    }

    通过换一台机的sn对比发现,没有在使用的机子是没有currentOrder的,所以就比较明了了,至少可以知道哪台机可以直接用了。

    所以,上Python

    Alt text

    发现是可以正常判断的,而且不难看出,currentOrder里面的remainTime就是剩余时间的秒数,所以我们把所有机子都放进去:

    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
    import requests
    import json

    ls = [
    ‘0000000000000A0007604201806230007999’,
    ‘0000000000000A0007604201806230010337’,
    ‘0000000000000A0007604201806230011227’,
    ‘0000000000000A0007604201806230011842’,
    ‘0000000000000A0007604201806230010248’,
    ‘0000000000000A0007604201806230011549’,
    ‘0000000000000A0007604201806230011246’
    ]
    url_f = ‘https://u.zhinengxiyifang.cn/api/LbUsers/3901151/getDeviceByQRCode?qrCode=http:%2F%2Fapp.littleswan.com%2Fu_download.html%3Ftype%3DUjing%26uuid%3D’

    headers = {
    ‘Host’: ‘u.zhinengxiyifang.cn’,
    ‘Accept’: ‘application/json, text/plain, */*’,
    ‘Connection’: ‘keep-alive’,
    ‘Cookie’: ‘acw_tc=76b20fe315672388832663385e351b31cb24d8e23fe7a2cf1765458201c140’,
    ‘User-Agent’: ‘Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 (4321215856)’,
    ‘Authorization’: ‘9CT3qmPW2yhEGNtfmT1cJdy2LKwOOQ87bt7f0J45Lo65plvOJAkYWmPKniMOFvnH’
    }

    for i in ls:
    url = url_f + i
    r = requests.get(url, headers=headers)
    rs = json.loads(r.text)

    if rs[‘result’][‘currentOrder’]:
    print(‘{}号机不可用({}分钟)’.format(rs[‘result’][‘no’],int(int(rs[‘result’][‘currentOrder’][‘remainTime’])/60)))
    else:
    print(‘{}号机可用’.format(rs[‘result’][‘no’]))
    Alt text

    这样就算是在算法上完成了,具体放到网站上可以用PHP之类的,还有一些小的细节我在这里并没有涉及到,比如说0分钟为刚付完款未开始之类的,这里只是大致演示一下原理,然后我也不是特别熟悉Python,若有不足之处欢迎指出。

  • Bilibili直播点歌台

    Bilibili直播点歌台

    上一篇文章有说到,瞎整个程序重装系统装错机了,整的就是这个程序——Bilibili直播点歌台。
    现在就来说说我是怎么瞎整的。

    我也是觉得无聊才发现有这么一个程序,所以就想玩一玩。但是GitHub上的这个项目,只不过是运行在树莓派上的,我当时也没想到要运行在云服务器上是多么困难。


    正文之前

    下面才是正文,如果懒得看正文的,直接上GayHub GitHub链接吧:
    github.com/yfme01/Music-Live-on-Bilibili
    里面有使用方法和使用需注意的地方。

    这里是预览:
    https://live.bilibili.com/8712608


    正文

    这里的东西主要是我记录整这玩意的过程用的,没有图片没什么可看性,有兴趣的可以当小说看看。

    先是走流程

    重装系统 -> 走教程安装依赖 -> git -> 配置 -> 运行

    然后就是发现问题与解决问题

    先是运行不起来,过了一会,有画面了,但是一直没有弹幕监控。然后呢,查看当前的screen发现弹幕也没有在运行,所以就找到弹幕监控的运行文件单独运行。终于有报错了,但是报错的出现只是一瞬间,所以我要把握好时间截图。几十次截图出来,就看到了好像是什么aiohttp出了啥问题。发现aiohttp是人家教程里的一个拓展库,于是又装了一次这个拓展库。结果还是有这么一个报错,后面就只好看看哪里有用到这个拓展库,发现用到的那个拓展库已经被注释掉了。。

    删掉那个拓展库的引用后,能正常运行了。

    强迫症的我看到了这个程序里的文件乱七八糟的,自然看不下去,于是我又找到了一个原版的重制版,只不过没有原版的新特性,而且还有一些Bug,先是修Bug,一些小小的修修补补就以为Bug修完了。

    运行了一天,到了晚上又发现一个致命Bug。就是说配置文件里面有个设置夜间模式不能点歌之类的东西,但是我明明设置关掉了,但是还是会有这个夜间模式。。

    于是我想把这个问题解决。

    因为本人没学过Python,所以看不出什么头绪,只能希望原版的没有问题。。

    所以我就开始了移植原版的功能文件,正好跟进以下新功能。先是把文件目录都改了。然后根据自己的习惯把画面中显示的专辑封面去掉了。果然就发现Bug了,送过的礼物读取不到、夜间模式还是存在、点歌会报错。

    只好分部来修复

    • 礼物读取不到:
      里面经常用到一个../xx/xx的目录格式,我又想到配置文件里面有个"path" : "xx/xx/xx",既然有这么个配置干嘛还要这么写,全部改成了paht相关的之后,发现能读取礼物了。
    • 夜间模式:
      夜间模式感觉我自己绝对不会使用的,所以判断是否为夜间的函数直接return False
    • 点歌会报错:
      前面有说,我把专辑封面图去掉了,也把获取封面图的接收变量去掉了。然后就会有一种情况:一个变量名去接收两个变量,所以把接收图片链接的变量还回去也解决了。

    这样就完美了,改了下ass生成的样式就算大功告成了。

    总结

    这就是元旦假期整的东西,看上去挺快的,但是实际上通了一宵,花了两个白天才弄完这些,我挺享受这个过程的,只是画的时间有点长了,还是因为自己能力不足,不过以后会在这些折腾中吸收经验的。