用Golang写个小工具,帮我把icam365上的云端视频下载下来

前阵子,我老婆一直在用icam365看家里摄像头的回放,想存几段宝宝走路的视频,可她每次都在手机上翻来翻去,截图、录屏,搞得模糊不说,还...

前阵子,我老婆一直在用icam365看家里摄像头的回放,想存几段宝宝走路的视频,可她每次都在手机上翻来翻去,截图、录屏,搞得模糊不说,还总说“算了算了太麻烦了”,我一听,这不就是让我动手吗?正好我在学Golang,心想能不能写个小程序,直接从icam365云端把视频扒下来。

说实话,一开始我也没底。icam365是专门做安防摄像头的平台,用户拍的视频都加密存储在云端,想下载并不像点个按钮那么简单,但我这个人嘛,遇到问题喜欢拆开来看,先把需求列出来:第一,要能登录icam365账号;第二,要能拿到视频列表;第三,要把视频下载到本地,听起来是不是挺像那么回事?其实每一步都不简单,但用Golang来做,心里还算踏实。

第一步:搞清楚icam365的接口长什么样

写这种工具,最核心的就是搞明白云端是怎么通信的,我翻了翻icam365的官方文档,也看了些论坛里的讨论,发现它们API大致是RESTful风格的,用Token来鉴权,你得先发个POST请求去登录,拿到一个长期有效的Token,然后再用这个Token去请求视频列表,最后一个一个下载。

这里我踩过一个坑——我一开始以为它的API是某一种标准格式,结果抓包一看,返回的数据居然是经过二次base64加密的,我当时就愣了:还能这么玩?不过想想也合理,摄像头视频涉及隐私,加密是必须的,好在Golang标准库里有encoding/base64,解密起来不算太复杂。

用Golang发HTTP请求

Golang里发HTTP请求主要靠net/http包,写起来很直观,你就创建一个http.Client,然后构造请求体、设置Header,再Do一下就行,下面是我写的核心代码片段(当然我把关键URL和密钥替换掉了哈,你懂的):

package main
import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
    "time"
)
type LoginResponse struct {
    Token string `json:"token"`
    UserID string `json:"user_id"`
}
func login(appKey, appSecret string) (string, error) {
    loginURL := "https://api.icam365.cn/v1/auth/login"
    data := fmt.Sprintf(`{"app_key":"%s","app_secret":"%s"}`, appKey, appSecret)
    resp, err := http.Post(loginURL, "application/json", strings.NewReader(data))
    if err != nil {
        return "", fmt.Errorf("登录请求失败: %v", err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    var loginResp LoginResponse
    if err := json.Unmarshal(body, &loginResp); err != nil {
        return "", fmt.Errorf("解析登录响应失败: %v", err)
    }
    return loginResp.Token, nil
}

你看,就这几行代码,登录就搞定了,但说实话,第一次运行时,我忘了处理Token过期的问题。Token一般有24小时有效期,如果你程序跑太久,突然Token失效了,那后面的请求就全挂了,后来我加了个自动刷新Token的逻辑,才算稳妥。

第二步:拿到视频列表,这步有点考验耐心

登录成功后,下一步就是获取设备列表,然后拿到某个时间段内的视频,icam365的设备ID是类似XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX这种格式的UUID,你需要先请求设备列表,找到你要的那个设备,然后再请求它下面的视频资源。

用Golang写个小工具,帮我把icam365上的云端视频下载下来

视频列表的API返回的是一个JSON数组,每个元素包含开始时间、结束时间、文件加密标志、视频片段大小等信息,这里有个关键点——每个视频片段大概1到3分钟,也就是说如果摄像头连续录制了24小时,你可能需要下载好几百个片段。

我一开始没注意到这一点,直接写了单线程下载,结果下载一个小时的视频花了四十分钟,后来改成用Goroutine并发下载,速度提高了很多倍,Goroutine是Golang的轻量线程,一个程序可以轻松开几百个,互不阻塞。

并发下载的核心思想

我大致思路是这样:先拿到所有视频片段的URL列表,然后创建一个带缓冲的channel,把URL扔进去,再起几个worker Goroutine,每个worker从channel里取URL,下载完成后把进度更新到另一个channel里,主函数就负责监听进度,直到所有任务完成。

下面是一个简化版本:

func downloadVideo(token, deviceID string, start, end time.Time, concurrency int) error {
    // 获取视频片段列表(略)
    segments := fetchVideoSegments(token, deviceID, start, end)
    urlChan := make(chan string, len(segments))
    doneChan := make(chan bool, len(segments))
    // 启动worker
    for i := 0; i < concurrency; i++ {
        go func() {
            for url := range urlChan {
                err := downloadSingleFile(url, token, deviceID)
                if err != nil {
                    log.Printf("下载失败: %v", err)
                }
                doneChan <- true
            }
        }()
    }
    // 发送URL
    for _, seg := range segments {
        urlChan <- seg.URL
    }
    close(urlChan)
    // 等待所有完成
    for i := 0; i < len(segments); i++ {
        <-doneChan
    }
    return nil
}

这样写下来,下载速度从单线程的每段平均8秒降到了每段0.5秒左右(看网络情况),我老婆在旁边看着进度条飞快跳动,抱怨说“早该写这程序了”。

第三步:解密和合并视频——最坑的一步来了

下载到本地的每个视频片段都是加密的,icam365的加密算法是AES-128-CBC,密钥是用户登录密码生成的32字节哈希,也就是说你得把密码做一次SHA256,取前16字节当密钥,这个信息我是在它们开发文档的某个角落里看到的,藏得很深。

解密的关键代码:

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
)
func decryptVideo(encryptedData []byte, password string) ([]byte, error) {
    key := sha256.Sum256([]byte(password))
    block, err := aes.NewCipher(key[:16])
    if err != nil {
        return nil, err
    }
    iv := encryptedData[:aes.BlockSize]
    ciphertext := encryptedData[aes.BlockSize:]
    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)
    // 去掉PKCS7填充
    padLen := int(ciphertext[len(ciphertext)-1])
    return ciphertext[:len(ciphertext)-padLen], nil
}

解密成功后,每个片段都是一个独立的视频文件,如果想要合并成一部完整的视频,需要把它们的二进制数据按时间顺序拼接起来,我用的是封装了MP4格式处理的库github.com/3d0c/gmf,但也可以直接用二进制拼接(注意:mp4文件有moov atom问题,所以最好用ffmpeg来合并)。

实际上我最后偷懒了:解密后直接调用本地ffmpeg来合并,这样最稳定。

import "os/exec"
func mergeVideos(files []string, output string) error {
    args := append([]string{"-i"}, files[0])
    for _, f := range files[1:] {
        args = append(args, "-cat", f)
    }
    args = append(args, "-c", "copy", output)
    cmd := exec.Command("ffmpeg", args...)
    return cmd.Run()
}

实际使用中遇到的几个坑

写代码是一回事,实操又是另一回事,我整理了几个常见的问题,用表格列出来吧:

问题 原因 解决办法
下载视频后播放不出来 解密密钥错误 确认密码是否经过SHA256处理
Token频繁过期 没有处理Token刷新 在请求前检查Token有效期
某个时间段没有视频 云端删除或未检测到运动 检查设备存储设置
合并后的视频声音与画面不同步 片段时间戳重叠或缺失 用ffprobe检查每个片段时长,按时间戳精确切割

有一次,我把密码记错了,结果解密出来全是乱码,播放器直接崩溃,我老婆拿着手机拍我,“看吧,程序员也不行嘛。”后来我加了密码验证功能——下载一个最小的视频片段,用用户输入的密码解密,如果能正常播放再继续下载。

还有一次,我忘了icam365的免费版只保留7天录像,而老婆想下载两个月前的视频,结果程序说“没有数据”,她还不信,以为我写错了,最后在app里一看,确实被自动覆盖了。

选Golang做这件事的理由

写这个工具的时候,我也想过用Python,但最后选了Golang,原因其实挺实在的:

  • 跨平台编译:Golang可以直接编译成Windows、macOS和Linux的可执行文件,给老婆用的话,只需给她一个.exe文件,不用装Python环境。
  • 并发天生方便:下载视频这种I/O密集任务,Goroutine简直为它而生,写起来很优雅。
  • 部署简单:编译后一个二进制文件,扔哪都行,我把它和ffmpeg一起打包在同一个文件夹里,拷贝到U盘就能用。

当然Golang也不是没有缺点,比如有些库不如Python丰富,像视频处理这块还得调外部程序,但对于我的需求来说,完全够用了。

最终文件结构

写完之后,我的项目大概长这样:

icam365-downloader/
├── main.go          - 主入口,命令行参数解析
├── auth.go          - 登录、Token管理
├── device.go        - 设备列表查询
├── video.go         - 视频列表获取
├── download.go      - 下载、解密、合并
├── utils.go         - 辅助函数(base64解码等)
└── README.md        - 使用说明

为了方便自己用,我还加了命令行参数,可以指定开始时间、结束时间、下载目录、并发数等。

icam365-downloader -start "2024-12-20 08:00" -end "2024-12-20 10:00" -out "./videos"

运行命令之后,程序就自动登录、拿列表、并发下载、解密、合并,最后输出一个完整的MP4文件。

写给想尝试的你

其实这个工具花了我一个周末的时间,写的过程里查了好多资料,也翻过icam365的一些技术文档(它们有开发者中心,不过文档写得比较简略),如果你也想自己写一个,建议先抓包分析一下API的具体结构,因为不同设备型号可能略有不同。

别忘了考虑法律和隐私问题,下载自己摄像头的视频是没问题的,但千万别拿去给别人用或者传播,我老婆看完了就让我把程序删了,说“留着你以后又瞎搞”。

嗯,现在她把那段1分23秒的宝宝走路视频循环播放给我看,我也挺开心的——虽然她不知道背后是那一堆Goroutine和AES解密在帮忙,其实嘛,写程序有时候就是这样,看起来是技术活儿,到头来还是为了生活里那点小事,你说是不是?

本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.uss1.cn/lvyou/1095.html

(2)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-07-01

    我是be365的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-07-01

    希望本篇文章《用Golang写个小工具,帮我把icam365上的云端视频下载下来》能对你有所帮助!

  • kyadmin
    kyadmin 2026-07-01

    本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名

  • kyadmin
    kyadmin 2026-07-01

    本文概览:前阵子,我老婆一直在用icam365看家里摄像头的回放,想存几段宝宝走路的视频,可她每次都在手机上翻来翻去,截图、录屏,搞得模糊不说,还...

    联系我们

    工作时间:周一至周五,9:30-18:30,节假日休息

    关注我们