├── website ├── IQ │ ├── config.yml │ ├── iq_test.go │ ├── iqy_test.go │ └── iq.go ├── Friday │ ├── config.yml │ ├── Friday_test.go │ └── Friday.go ├── XiGUA │ ├── config.yml │ └── xg_test.go ├── web.go ├── QQTV │ ├── proto │ │ ├── env.proto │ │ ├── wetv.proto │ │ ├── trpc_video_detail_list.proto │ │ ├── request_base.proto │ │ └── basic_data.proto │ ├── TXTV_test.go │ ├── TXTV.go │ ├── env.pb.go │ └── WETV.go ├── yangshipin │ ├── tars │ │ ├── ResponseCommand.tars │ │ ├── VideoDetailsRequest.tars │ │ ├── ONATVDetailsVideoSquareListItem.tars │ │ └── RequestCommand.tars │ ├── ysp_test.go │ └── ysp.go ├── Hami │ └── hami_test.go ├── KKTV │ ├── kktv_test.go │ └── kktv.go ├── VIU │ └── viu_test.go ├── MGTV │ ├── mgtv_test.go │ └── mgtv.go ├── MytvSuper │ ├── mytvsuper_test.go │ └── mytvsuper.go ├── LETV │ ├── letv_test.go │ └── letv.go ├── Litv │ ├── litv_test.go │ └── litv.go ├── MyVideo │ ├── myvideo_test.go │ └── myvideo.go ├── Bilibili │ └── b_test.go ├── YOUKUTV │ └── yk_test.go ├── VIKI │ └── viki_test.go └── MIGU │ ├── mg_test.go │ └── mg.go ├── img └── img.png ├── config.yaml ├── server ├── proto │ ├── ddddlist.sh │ └── ddddlist.proto ├── data.go └── trpc │ ├── server.go │ └── ddddlist.trpc.go ├── cmd ├── client │ ├── a.http │ ├── client.go │ └── a.sh └── server │ ├── app.go │ └── a.sh ├── .github ├── ISSUE_TEMPLATE │ └── bug.md └── workflows │ ├── IQ.yml │ ├── VIU.yml │ ├── Hami.yml │ ├── KKTV.yml │ ├── LETV.yml │ ├── Litv.yml │ ├── MGTV.yml │ ├── MIGU.yml │ ├── QQTV.yml │ ├── VIKI.yml │ ├── XiGUA.yml │ ├── Friday.yml │ ├── YOUKUTV.yml │ ├── Bilibili.yml │ ├── MyVideo.yml │ ├── yangshipin.yml │ ├── MytvSuper.yml │ ├── client.yml │ └── server.yml ├── utils ├── jwt │ ├── jwt_test.go │ └── jwt.go ├── tool.go └── sesssion │ └── session.go ├── LICENSE.txt ├── config └── config.go ├── go.mod ├── trpc_go.yaml ├── CODE_OF_CONDUCT.md └── README.md /website/IQ/config.yml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /website/Friday/config.yml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /website/XiGUA/config.yml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /img/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tontonnow/ddddhmlist/HEAD/img/img.png -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | Mac: '00:00:00:00:00:00' 2 | AndroidId: '0000000000000000' 3 | Proxy: '' -------------------------------------------------------------------------------- /server/proto/ddddlist.sh: -------------------------------------------------------------------------------- 1 | trpc create -p ddddlist.proto -o ./out --rpconly --mock=false --nogomod=true -------------------------------------------------------------------------------- /cmd/client/a.http: -------------------------------------------------------------------------------- 1 | GET http://127.0.0.1:8001/api/v1/ddddlist?url=https://v.youku.com/v_show/id_XNTk1MjQxNjQzMg==.html 2 | Content-Type: application/json 3 | -------------------------------------------------------------------------------- /website/web.go: -------------------------------------------------------------------------------- 1 | package website 2 | 3 | import "github.com/Tontonnow/ddddhmlist/website/Friday" 4 | 5 | func Init() error { 6 | err := Friday.RefreshToken() 7 | if err != nil { 8 | return err 9 | } 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /website/QQTV/proto/env.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package QQTV; 4 | option go_package = "../../QQTV"; 5 | message EnvInfo { 6 | string name = 1; 7 | string user = 2; 8 | string detail = 3; 9 | map metadata = 4; 10 | } 11 | -------------------------------------------------------------------------------- /server/data.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | func (x *Data) Len() int { 4 | return len(x.VideoList) 5 | } 6 | func (x *Data) Less(i, j int) bool { 7 | if x.VideoList[i].Episode == x.VideoList[j].Episode { 8 | return x.VideoList[i].EpisodeId < x.VideoList[j].EpisodeId 9 | } 10 | return x.VideoList[i].Episode < x.VideoList[j].Episode 11 | } 12 | func (x *Data) Swap(i, j int) { 13 | x.VideoList[i], x.VideoList[j] = x.VideoList[j], x.VideoList[i] 14 | } 15 | -------------------------------------------------------------------------------- /website/yangshipin/tars/ResponseCommand.tars: -------------------------------------------------------------------------------- 1 | #include "RequestCommand.tars" 2 | module Cctv 3 | { 4 | struct ResponseHead 5 | { 6 | 0 require int requestId; 7 | 1 require int cmdId; 8 | 2 require int errCode; 9 | 3 require string sUserid; 10 | }; 11 | struct ResponseCommand 12 | { 13 | 0 require ResponseHead head; 14 | 1 optional vector body; 15 | 2 optional Cctv::BusinessHead businessHead; 16 | }; 17 | }; -------------------------------------------------------------------------------- /cmd/server/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Tontonnow/ddddhmlist/config" 5 | "github.com/Tontonnow/ddddhmlist/server/trpc" 6 | "github.com/Tontonnow/ddddhmlist/website" 7 | "trpc.group/trpc-go/trpc-go/log" 8 | ) 9 | 10 | var ( 11 | name = "ddddlist" 12 | version = "0.0.1" 13 | ) 14 | 15 | func Init() { 16 | config.InitConfig() 17 | err := website.Init() 18 | if err != nil { 19 | return 20 | } 21 | trpc.Init() 22 | } 23 | 24 | func main() { 25 | log.Infof("Starting %s %s", name, version) 26 | Init() 27 | } 28 | -------------------------------------------------------------------------------- /website/XiGUA/xg_test.go: -------------------------------------------------------------------------------- 1 | package XiGUA 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetMateInfo(t *testing.T) { 12 | r, err := GetMateInfo(ctx, "https://www.ixigua.com/7117099761831772686?logTag=84d96f5b647aa7e1fb48") 13 | assert.Equal(t, err, 0) 14 | assert.Equal(t, "7348752092212757019", r.SeriesId) 15 | assert.Equal(t, "v02043g10000cnu23e3c77u49t96guj0", r.VideoList[0].Extra["vid"]) 16 | } 17 | -------------------------------------------------------------------------------- /website/Hami/hami_test.go: -------------------------------------------------------------------------------- 1 | package Hami 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetMateInfo(t *testing.T) { 12 | t.Run("GetMateInfo", func(t *testing.T) { 13 | v, err := GetMateInfo(ctx, "https://hamivideo.hinet.net/hamivideo/product/152279.do?cs=2") 14 | assert.Equal(t, err, 0) 15 | assert.Equal(t, "152279", v.SeriesId) 16 | assert.Equal(t, "火神的眼淚", v.SeriesTitle) 17 | assert.Equal(t, "4k", v.VideoList[0].Extra["quality"]) 18 | 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: bug 3 | about: 问题反馈 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **描述问题** 11 | 12 | 请简单明了的描述问题,最好能提供复现步骤 13 | 14 | **复现问题** 15 | 1. 你干了什么 16 | 2. 发生了什么 17 | 3. 发生频率 18 | 19 | **你想要的结果** 20 | 21 | 对您期望发生的情况的清晰简洁的描述。 22 | 23 | **截图或日志** 24 | 如果适用,请添加屏幕截图或日志以帮助解释您的问题。 25 | 26 | **环境信息** 27 | - 操作系统: [例如 Windows] 28 | - 版本: [例如 win10 1903] 29 | - 软件版本: [例如 1.0.0] 30 | - 其他信息 31 | 32 | **自我检查** 33 | - [ ] 是否会编程 34 | - [ ] 是否会搜索 35 | - [ ] 我已经搜索了已有的问题 36 | - [ ] 我已经查看了文档 37 | - [ ] 我觉得自行解决不了 38 | - [ ] 我觉得别人能帮我解决 39 | - [ ] 我会耐心等待 40 | - [ ] 我会心怀感激 41 | -------------------------------------------------------------------------------- /.github/workflows/IQ.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: iq 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/IQ 26 | -------------------------------------------------------------------------------- /website/KKTV/kktv_test.go: -------------------------------------------------------------------------------- 1 | package KKTV 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetEpq(t *testing.T) { 12 | url := "https://kktv.me/titles/06001673" 13 | d, err := GetMateInfo(ctx, url) 14 | assert.Equal(t, err, 0) 15 | assert.NotNil(t, d) 16 | assert.Equal(t, "葬送的芙莉蓮", d.SeriesTitle) 17 | assert.Equal(t, "06001673", d.SeriesId) 18 | assert.Equal(t, "06001673010001", d.VideoList[0].EpisodeId) 19 | assert.Contains(t, d.VideoList[0].Extra["dash"], "https://theater.kktv.com.tw/73/06001673010001") 20 | } 21 | -------------------------------------------------------------------------------- /website/yangshipin/tars/VideoDetailsRequest.tars: -------------------------------------------------------------------------------- 1 | module Cctv 2 | { 3 | struct VideoDetailsRequest 4 | { 5 | 0 optional string lid; 6 | 1 optional string cid; 7 | 2 optional string vid; 8 | 3 optional string historyVid; 9 | 4 optional map expansionMap; 10 | 5 optional string sessionId; 11 | 6 optional string refreshContext; 12 | 7 optional string pageContext; 13 | 8 optional string serverFrom; 14 | 9 optional string dataKey; 15 | }; 16 | }; -------------------------------------------------------------------------------- /.github/workflows/VIU.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: VIU 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/VIU -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/Hami.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Hami 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/Hami -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/KKTV.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: KKTV 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/KKTV -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/LETV.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: LETV 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/LETV -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/Litv.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Litv 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/Litv -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/MGTV.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: MGTV 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/MGTV -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/MIGU.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: MIGU 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/MIGU -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/QQTV.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: QQTV 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/QQTV -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/VIKI.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: VIKI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/VIKI -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/XiGUA.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: XiGUA 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/XiGUA -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /website/VIU/viu_test.go: -------------------------------------------------------------------------------- 1 | package Viu 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/stretchr/testify/assert" 7 | "runtime/debug" 8 | "testing" 9 | ) 10 | 11 | var ctx = context.WithValue(context.Background(), "requestId", "test") 12 | 13 | func TestGetViuSeries(t *testing.T) { 14 | defer func() { 15 | if err := recover(); err != nil { 16 | stack := string(debug.Stack()) 17 | fmt.Println(err) 18 | fmt.Println(stack) 19 | 20 | } 21 | }() 22 | r, err := GetMateInfo(ctx, "https://www.viu.com/ott/hk/zh/vod/2336233/%E5%85%A8%E7%9F%A5%E5%B9%B2%E9%A0%90%E8%A6%96%E8%A7%92-2024") 23 | assert.Equal(t, err, 0) 24 | assert.Equal(t, "72465", r.SeriesId) 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/Friday.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Friday 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/Friday -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/YOUKUTV.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: yk 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/YOUKUTV -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/Bilibili.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Bilibili 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/Bilibili -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/MyVideo.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: MyVideo 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/MyVideo -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/yangshipin.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: ysp 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/yangshipin -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /.github/workflows/MytvSuper.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: MytvSuper 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22' 23 | 24 | - name: Test 25 | run: go test -v ./website/MytvSuper -race -coverpkg=./... -coverprofile=coverage.txt 26 | -------------------------------------------------------------------------------- /utils/jwt/jwt_test.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestParseJwtWithClaims(t *testing.T) { 9 | jwtStr := "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJVdWlkIjoiMjAyMDExMjcxNDQ2MjQ1NzJ4WGtiV0J1S1kxMDAwMzEiLCJWZXJzaW9uIjoiOC4yLjAiLCJEZXZpY2VJZCI6ImQ4NDBhMjY2NWJkN2Q0OTAzMjc5N2MxZmEyODZjYTAyMTEwZCIsIk1hY0FkZHIiOiIwMDpEQjpBNzozRTo5RTowOCIsIlNka0ludFZlciI6IjI4IiwiZXhwIjoxNzEyNTc0ODQyfQ.ZmsoLXqCIUOs6c1lxKNHhGBV2Tt86nUNLMVz3sQmFHLse_eRmygegFPNq7-CplwTtANPkHhZacU9vevVoaWoi7VI0IlX1z2O7CySAVh8EfaDlw6X51EaMUX49Hhrw_HwFB_r4_3wpEqsklMgYya37qRMURAcpn78BTQYCWNnOC8" 10 | claims, err := ParseJwtWithClaims(jwtStr) 11 | if err != nil { 12 | fmt.Println(err) 13 | return 14 | } 15 | fmt.Println(claims) 16 | } 17 | -------------------------------------------------------------------------------- /website/MGTV/mgtv_test.go: -------------------------------------------------------------------------------- 1 | package MGTV 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetVideoInfo(t *testing.T) { 12 | t.Log("TestGetVideoInfo") 13 | t.Run("TestGetVideoInfo", func(t *testing.T) { 14 | t.Log("Test for movie") 15 | u := "https://www.mgtv.com/b/598961/20728335.html?fpa=1663&fpos=&lastp=ch_movie" 16 | _, err := GetMateInfo(ctx, u) 17 | assert.Equal(t, err, 0) 18 | }) 19 | t.Run("TestGetVideoInfo", func(t *testing.T) { 20 | t.Log("Test for TV series") 21 | u := "https://www.mgtv.com/b/338497/8398205.html" 22 | _, err := GetMateInfo(ctx, u) 23 | assert.Equal(t, err, 0) 24 | }) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /website/MytvSuper/mytvsuper_test.go: -------------------------------------------------------------------------------- 1 | package MytvSuper 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | u = "https://www.mytvsuper.com/tc/programme/tokyoloveactstory0001_139100/%E6%9D%B1%E4%BA%AC%E6%84%9B%E6%83%85%E5%8B%95%E4%BD%9C%E6%95%85%E4%BA%8B-(4K%E7%89%88)" 11 | ctx = context.WithValue(context.Background(), "requestId", "test") 12 | ) 13 | 14 | func TestGetProgrammeId(t *testing.T) { 15 | got := getProgrammeId(u) 16 | assert.Equal(t, "139100", got) 17 | } 18 | 19 | func TestGetMateInfo(t *testing.T) { 20 | r, err := GetMateInfo(ctx, u) 21 | assert.Equal(t, err, 0) 22 | assert.Equal(t, "東京愛情動作故事 (4K版)", r.SeriesTitle) 23 | assert.Equal(t, "748903", r.VideoList[0].Extra["video_id"]) 24 | } 25 | -------------------------------------------------------------------------------- /website/LETV/letv_test.go: -------------------------------------------------------------------------------- 1 | package LETV 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetList(t *testing.T) { 12 | t.Run("剧集", func(t *testing.T) { 13 | url := "https://www.le.com/ptv/vplay/77532622.html" 14 | d, err := GetMateInfo(ctx, url) 15 | assert.Equal(t, err, 0) 16 | assert.Equal(t, "10070229", d.SeriesId) 17 | assert.Equal(t, "川流", d.SeriesTitle) 18 | }) 19 | t.Run("电影", func(t *testing.T) { 20 | url := "https://www.le.com/ptv/vplay/77515961.html" 21 | d, err := GetMateInfo(ctx, url) 22 | assert.Equal(t, err, 0) 23 | assert.Equal(t, "10069543", d.SeriesId) 24 | assert.Equal(t, "速度与激战", d.SeriesTitle) 25 | 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /website/QQTV/proto/wetv.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package QQTV; 3 | option go_package = "../../QQTV"; 4 | message Request { 5 | int32 version = 1; 6 | int32 call_type = 2; 7 | int32 request_id = 3; 8 | int32 timeout = 4; 9 | bytes caller = 5; 10 | bytes callee = 6; 11 | bytes func = 7; 12 | int32 message_type = 8; 13 | map trans_info = 9; 14 | int32 content_type = 10; 15 | int32 content_encoding = 11; 16 | } 17 | message Response { 18 | int32 version = 1; 19 | int32 call_type = 2; 20 | int32 request_id = 3; 21 | int32 ret = 4; 22 | int32 func_ret = 5; 23 | string error_msg = 6; 24 | int32 message_type = 7; 25 | map trans_info = 8; 26 | int32 content_type = 9; 27 | int32 content_encoding = 10; 28 | } 29 | -------------------------------------------------------------------------------- /utils/jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "errors" 5 | "github.com/golang-jwt/jwt/v5" 6 | ) 7 | 8 | var ( 9 | Key = "secret" 10 | ) 11 | 12 | func ParseJwtWithClaims(jwtStr string, options ...jwt.ParserOption) (jwt.Claims, error) { 13 | mc := jwt.MapClaims{} 14 | token, err := jwt.ParseWithClaims(jwtStr, mc, func(token *jwt.Token) (interface{}, error) { 15 | return Key, nil 16 | }, options...) 17 | if err != nil { 18 | return token.Claims, err 19 | } 20 | if !token.Valid { 21 | return nil, errors.New("invalid token") 22 | } 23 | return token.Claims, nil 24 | } 25 | func MakeJwtWithClaims(claims jwt.Claims) (string, error) { 26 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 27 | tokenStr, err := token.SignedString(Key) 28 | if err != nil { 29 | return "", err 30 | } 31 | return tokenStr, nil 32 | } 33 | -------------------------------------------------------------------------------- /website/Litv/litv_test.go: -------------------------------------------------------------------------------- 1 | package Litv 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetList(t *testing.T) { 12 | t.Run("剧集", func(t *testing.T) { 13 | url := "https://www.litv.tv/comic/watch/VOD00297197" 14 | d, err := GetMateInfo(ctx, url) 15 | assert.Equal(t, err, 0) 16 | assert.Equal(t, "64742", d.SeriesId) 17 | assert.Equal(t, "咒術迴戰 第二季", d.SeriesTitle) 18 | assert.Equal(t, "VOD00298361", d.VideoList[0].EpisodeId) 19 | }) 20 | t.Run("电影", func(t *testing.T) { 21 | url := "https://www.litv.tv/movie/watch/VOD00321615" 22 | d, err := GetMateInfo(ctx, url) 23 | assert.Equal(t, err, 0) 24 | assert.Equal(t, "69114", d.SeriesId) 25 | assert.Equal(t, "嫌疑犯X", d.SeriesTitle) 26 | assert.Contains(t, d.Extra["assets"], "000000M001_5000K") 27 | 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /website/MyVideo/myvideo_test.go: -------------------------------------------------------------------------------- 1 | package MyVideo 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetMateInfo(t *testing.T) { 12 | 13 | t.Run("电影", func(t *testing.T) { 14 | u := "https://www.myvideo.net.tw/details/0/385248" 15 | v, err := GetMateInfo(ctx, u) 16 | assert.Equal(t, err, 0) 17 | assert.False(t, v.IsSeries) 18 | assert.Equal(t, "385248", v.SeriesId) 19 | assert.Equal(t, "虛擬獵殺令", v.SeriesTitle) 20 | }) 21 | t.Run("剧集", func(t *testing.T) { 22 | u := "https://www.myvideo.net.tw/details/3/26950" 23 | v, err := GetMateInfo(ctx, u) 24 | assert.Equal(t, err, 0) 25 | assert.True(t, v.IsSeries) 26 | assert.Equal(t, "26950", v.SeriesId) 27 | assert.Equal(t, "王者天下 第五季", v.SeriesTitle) 28 | assert.Equal(t, "392737", v.VideoList[0].EpisodeId) 29 | 30 | }) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /website/yangshipin/ysp_test.go: -------------------------------------------------------------------------------- 1 | package yangshipin 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetEpq(t *testing.T) { 12 | pid := "600002264" 13 | var DataList []*CnYangshipinOmstvCommonProtoProgramModel_Program 14 | DataList = GetEpg(pid) 15 | assert.Equal(t, "18229996", DataList[0].ProgramId) //不准确,仅供参考 16 | } 17 | 18 | func TestGetEpgId(t *testing.T) { 19 | Data := GetEpgId() 20 | assert.NotNil(t, Data) 21 | } 22 | 23 | func TestGetList(t *testing.T) { 24 | u := "https://www.yangshipin.cn/#/video/home?vid=m000071dlup&cid=lbl89jz9be9npgx" 25 | list, err := GetMateInfo(ctx, u) 26 | assert.Equal(t, err, 0) 27 | assert.Equal(t, "lbl89jz9be9npgx", list.SeriesId) 28 | assert.Equal(t, "千秋诗颂(4K)", list.SeriesTitle) 29 | assert.Equal(t, "m000071dlup", list.VideoList[0].EpisodeId) 30 | } 31 | -------------------------------------------------------------------------------- /website/IQ/iq_test.go: -------------------------------------------------------------------------------- 1 | package IQ 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestNewIq(t *testing.T) { 9 | i := NewIq() 10 | err := i.RefreshToken() 11 | assert.Nil(t, err) 12 | 13 | } 14 | func TestGetIqMateInfo(t *testing.T) { 15 | 16 | t.Run("电影", func(t *testing.T) { 17 | u := "https://www.iq.com/album/%E5%BA%9F%E5%93%81%E9%A3%9E%E8%BD%A6-2024-1bjmxliiwxc?lang=zh_cn" 18 | v, err := GetIqMateInfo(ctx, u) 19 | assert.Equal(t, err, 0) 20 | assert.Equal(t, "4876065378052700", v.SeriesId) 21 | assert.Equal(t, "废品飞车", v.SeriesTitle) 22 | }) 23 | t.Run("剧集", func(t *testing.T) { 24 | u := "https://www.iq.com/play/%E7%B9%81%E6%98%9F%E4%B9%8B%E5%9F%8E-%E7%AC%AC1%E9%9B%86-1b7fo0drgkc?lang=zh_cn" 25 | v, err := GetIqMateInfo(ctx, u) 26 | assert.Equal(t, err, 0) 27 | assert.Equal(t, "3548480898144901", v.SeriesId) 28 | assert.Equal(t, "繁星之城", v.SeriesTitle) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Tontonnow/ddddhmlist/server" 5 | pb "github.com/Tontonnow/ddddhmlist/server/trpc" 6 | "trpc.group/trpc-go/trpc-go" 7 | "trpc.group/trpc-go/trpc-go/client" 8 | "trpc.group/trpc-go/trpc-go/log" 9 | ) 10 | 11 | func main() { 12 | cfg, err := trpc.LoadConfig(trpc.ServerConfigPath) 13 | if err != nil { 14 | panic("load config fail: " + err.Error()) 15 | } 16 | trpc.SetGlobalConfig(cfg) 17 | if err := trpc.Setup(cfg); err != nil { 18 | panic("setup plugin fail: " + err.Error()) 19 | } 20 | proxy := pb.NewDDDDhmClientProxy( 21 | client.WithTarget("ip://127.0.0.1:8002"), 22 | client.WithProtocol("trpc"), 23 | ) 24 | ctx := trpc.BackgroundContext() 25 | reply, err := proxy.DdddList(ctx, &server.Request{ 26 | Url: "https://v.youku.com/v_show/id_XNTk1MjQxNjQzMg==.html", 27 | }) 28 | if err != nil { 29 | log.Fatalf("err: %v", err) 30 | } 31 | log.Debugf("simple rpc receive: %+v", reply) 32 | } 33 | -------------------------------------------------------------------------------- /website/Bilibili/b_test.go: -------------------------------------------------------------------------------- 1 | package Bilibili 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestGetMateInfo(t *testing.T) { 10 | var ctx = context.WithValue(context.Background(), "requestId", "test") 11 | t.Run("drama", func(t *testing.T) { 12 | url := "https://www.bilibili.com/bangumi/play/ep811437" 13 | d, err := GetMateInfo(ctx, url) 14 | assert.Equal(t, err, 0) 15 | assert.True(t, d.IsSeries) 16 | assert.Equal(t, "咸鱼哥 第二季", d.SeriesTitle) 17 | assert.Equal(t, "1447505461", d.VideoList[0].EpisodeId) 18 | 19 | }) 20 | t.Run("movie", func(t *testing.T) { 21 | url := "https://www.bilibili.com/bangumi/play/ss45822" 22 | d, err := GetMateInfo(ctx, url) 23 | assert.Equal(t, err, 0) 24 | assert.False(t, d.IsSeries) 25 | assert.Equal(t, "金手指", d.SeriesTitle) 26 | assert.Equal(t, "1432240939", d.VideoList[0].EpisodeId) 27 | assert.Equal(t, "814488", d.VideoList[0].Extra["ep_id"]) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Tontonnow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /website/YOUKUTV/yk_test.go: -------------------------------------------------------------------------------- 1 | package YOUKUTV 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetMateInfo(t *testing.T) { 12 | t.Run("剧集", func(t *testing.T) { 13 | U := "https://v.youku.com/v_show/id_XMzk1NjM1MjAw.html?s=cc003400962411de83b1&spm=a2hje.13141534.1_3.d_1_1&scm=20140719.apircmd.239064.video_XMzk1NjM1MjAw" 14 | d, err := GetMateInfo(ctx, U) 15 | assert.Equal(t, err, 0) 16 | assert.Equal(t, "名侦探柯南", d.SeriesTitle) 17 | //assert.Equal(t, "XMzk1NjM1MjAw", d.VideoList[0].EpisodeId) 18 | assert.True(t, len(d.VideoList) > 1170) //可能和ip有关 19 | }) 20 | t.Run("电影", func(t *testing.T) { 21 | U := "https://v.youku.com/v_show/id_XNjAxNzc3NjE3Ng==.html" 22 | d, err := GetMateInfo(ctx, U) 23 | assert.Equal(t, err, 0) 24 | assert.Equal(t, "奇迹·笨小孩 IMAX版", d.SeriesTitle) 25 | }) 26 | t.Run("国际版", func(t *testing.T) { 27 | U := "https://www.youku.tv/v/v_show/id_XNjM4MTM3MDgwNA==.html" 28 | d, err := GetMateInfo(ctx, U) 29 | assert.Equal(t, err, 0) 30 | assert.Equal(t, "花间令 简体版 01", d.VideoList[0].EpisodeTitle) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /website/VIKI/viki_test.go: -------------------------------------------------------------------------------- 1 | package VIKI 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetId(t *testing.T) { 12 | 13 | t.Run("Test GetId", func(t *testing.T) { 14 | u := "https://www.viki.com/movies/38677c-bo-knows-love" 15 | movies, id := GetId(u) 16 | assert.Equal(t, "movies", movies) 17 | assert.Equal(t, "38677c", id) 18 | }) 19 | t.Run("Test GetId", func(t *testing.T) { 20 | u := "https://www.viki.com/tv/39828c-decline" 21 | movies, id := GetId(u) 22 | assert.Equal(t, "tv", movies) 23 | assert.Equal(t, "39828c", id) 24 | }) 25 | } 26 | func TestGetMateInfo(t *testing.T) { 27 | t.Run("Test movies", func(t *testing.T) { 28 | u := "https://www.viki.com/movies/38677c-bo-knows-love" 29 | movies, err := GetMateInfo(ctx, u) 30 | assert.Equal(t, err, 0) 31 | assert.Equal(t, "我叫梁山伯", movies.SeriesTitle) 32 | }) 33 | t.Run("Test tv", func(t *testing.T) { 34 | u := "https://www.viki.com/tv/39828c-decline" 35 | movies, err := GetMateInfo(ctx, u) 36 | assert.Equal(t, err, 0) 37 | assert.Equal(t, "江湖少年诀", movies.SeriesTitle) 38 | assert.Equal(t, "1246978v", movies.VideoList[0].EpisodeId) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /website/QQTV/TXTV_test.go: -------------------------------------------------------------------------------- 1 | package QQTV 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var ctx = context.WithValue(context.Background(), "requestId", "test") 10 | 11 | func TestGetPlayInfo(t *testing.T) { 12 | t.Run("ExtractCid", func(t *testing.T) { 13 | url := "https://v.qq.com/x/cover/mzc002000mc2t5k/m004878ddyw.html" 14 | cid := ExtractCid(url) 15 | assert.Equal(t, "mzc002000mc2t5k", cid) 16 | }) 17 | t.Run("qqtv", func(t *testing.T) { 18 | url := "https://m.v.qq.com/x/m/play?vid=c0048s8n6p3&cid=mzc00200as5tv65&ptag=share_11_11&url_from=share&second_share=0&share_from=qqf&pgid=page_detail&mod_id=mod_toolbar_new" 19 | d, err := GetMateInfo(ctx, url) 20 | assert.Equal(t, err, 0) 21 | assert.Equal(t, "mzc00200as5tv65", d.SeriesId) 22 | assert.Equal(t, "哈哈哈哈哈 第4季", d.SeriesTitle) 23 | assert.Equal(t, "m0048l524x0", d.VideoList[0].EpisodeId) 24 | }) 25 | t.Run("wetv", func(t *testing.T) { 26 | d, err := GetWeTvMateInfo(ctx, "https://wetv.vip/zh-cn/play/0cf9i81kpms8469-%E5%AD%A4%E7%94%B7%E5%AF%A1%E5%A5%B3/q0035x0szr1-%E5%AD%A4%E7%94%B7%E5%AF%A1%E5%A5%B3") 27 | assert.Equal(t, err, 0) 28 | assert.Equal(t, "0cf9i81kpms8469", d.SeriesId) 29 | assert.Equal(t, "孤男寡女", d.SeriesTitle) 30 | }) 31 | return 32 | } 33 | -------------------------------------------------------------------------------- /website/Friday/Friday_test.go: -------------------------------------------------------------------------------- 1 | package Friday 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestGetEpq(t *testing.T) { 10 | var ctx = context.WithValue(context.Background(), "requestId", "test") 11 | err := RefreshToken() 12 | assert.Nil(t, err) 13 | t.Run("drama", func(t *testing.T) { 14 | url := "https://video.friday.tw/drama/detail/3356" 15 | d, err := GetMateInfo(ctx, url) 16 | assert.Equal(t, err, 0) 17 | assert.True(t, d.IsSeries) 18 | assert.Equal(t, "青春時代", d.SeriesTitle) 19 | assert.Equal(t, "103581", d.VideoList[0].Extra["ContentId"]) 20 | assert.Equal(t, "2", d.VideoList[0].Extra["ContentType"]) 21 | assert.Equal(t, "114913", d.VideoList[0].Extra["StreamingId"]) 22 | assert.Equal(t, "2", d.VideoList[0].Extra["StreamingType"]) 23 | 24 | }) 25 | t.Run("movie", func(t *testing.T) { 26 | url := "https://video.friday.tw/movie/detail/102915/%E6%B0%B4%E9%AC%BC" 27 | d, err := GetMateInfo(ctx, url) 28 | assert.Equal(t, err, 0) 29 | assert.False(t, d.IsSeries) 30 | assert.Equal(t, "水鬼", d.SeriesTitle) 31 | assert.Equal(t, "102915", d.Extra["ContentId"]) 32 | assert.Equal(t, "1", d.Extra["ContentType"]) 33 | assert.Equal(t, "114169", d.Extra["StreamingId"]) 34 | assert.Equal(t, "2", d.Extra["StreamingType"]) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/client.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: build-go-binary 5 | 6 | on: 7 | release: 8 | types: [ created ] 9 | workflow_dispatch: 10 | 11 | 12 | 13 | jobs: 14 | releases-matrix: 15 | name: Release Go Binary 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | # build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 20 | goos: [linux, windows, darwin] 21 | goarch: ["386", amd64, arm64] 22 | exclude: 23 | - goarch: "386" 24 | goos: darwin 25 | - goarch: arm64 26 | goos: windows 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: wangyoucao577/go-release-action@v1.49 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | goos: ${{ matrix.goos }} 33 | goarch: ${{ matrix.goarch }} 34 | goversion: '1.22' 35 | binary_name: 'ddddlist' 36 | project_path: "./cmd/client" 37 | executable_compression: upx -9 38 | ldflags: '-s -w ' 39 | extra_files: config.yaml trpc_go.yaml README.md LICENSE.txt 40 | 41 | -------------------------------------------------------------------------------- /.github/workflows/server.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: build-go-binary 5 | 6 | on: 7 | release: 8 | types: [ created ] 9 | workflow_dispatch: 10 | 11 | 12 | 13 | jobs: 14 | releases-matrix: 15 | name: Release Go Binary 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | # build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 20 | goos: [linux, windows, darwin] 21 | goarch: ["386", amd64, arm64] 22 | exclude: 23 | - goarch: "386" 24 | goos: darwin 25 | - goarch: arm64 26 | goos: windows 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: wangyoucao577/go-release-action@v1.49 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | goos: ${{ matrix.goos }} 33 | goarch: ${{ matrix.goarch }} 34 | goversion: '1.22' 35 | binary_name: 'ddddlist' 36 | project_path: "./cmd/server" 37 | executable_compression: upx -9 38 | ldflags: '-s -w ' 39 | extra_files: config.yaml trpc_go.yaml README.md LICENSE.txt 40 | 41 | -------------------------------------------------------------------------------- /utils/tool.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/google/uuid" 6 | "math/rand" 7 | ) 8 | 9 | func GenerateRandomString(length int) string { 10 | characters := "0123456789abcdef" 11 | return GenerateRandomStringWithCharset(length, characters) 12 | } 13 | func GenerateRandomNumber(length int) string { 14 | characters := "0123456789" 15 | return GenerateRandomStringWithCharset(length, characters) 16 | } 17 | func GenerateRandomStringWithCharset(length int, charset string) string { 18 | result := make([]byte, length) 19 | for i := range result { 20 | result[i] = charset[rand.Intn(len(charset))] 21 | } 22 | return string(result) 23 | } 24 | 25 | func GenerateRandomMac() string { 26 | return fmt.Sprintf("%s:%s:%s:%s:%s:%s", GenerateRandomString(2), GenerateRandomString(2), GenerateRandomString(2), GenerateRandomString(2), GenerateRandomString(2), GenerateRandomString(2)) 27 | } 28 | 29 | func GenerateRandomAndroidId() string { 30 | return GenerateRandomString(16) 31 | } 32 | 33 | func GenerateRandomImei() string { 34 | return GenerateRandomNumber(15) 35 | } 36 | 37 | func GenerateRandomUdid() string { 38 | return fmt.Sprintf("%s-%s-%s-%s-%s", GenerateRandomString(8), GenerateRandomString(4), GenerateRandomString(4), GenerateRandomString(4), GenerateRandomString(12)) 39 | } 40 | func GenerateUUID() string { 41 | return uuid.New().String() 42 | } 43 | -------------------------------------------------------------------------------- /utils/sesssion/session.go: -------------------------------------------------------------------------------- 1 | package sesssion 2 | 3 | import ( 4 | "github.com/Tontonnow/ddddhmlist/config" 5 | "github.com/wangluozhe/requests" 6 | "github.com/wangluozhe/requests/models" 7 | "github.com/wangluozhe/requests/url" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | Pxy = "" 13 | ) 14 | 15 | type Client struct { 16 | Session *requests.Session 17 | } 18 | 19 | func NewClient(w config.WebConfig) *Client { 20 | if !config.IsLoad { 21 | config.InitConfig() 22 | config.IsLoad = true 23 | } 24 | session := requests.NewSession() 25 | session.Proxies = Pxy 26 | session.Headers.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0") 27 | if w.Authorization != "" { 28 | if strings.Contains(w.Authorization, "Bearer") { 29 | session.Headers.Set("Authorization", w.Authorization) 30 | } else { 31 | session.Headers.Set("Authorization", "Bearer "+w.Authorization) 32 | } 33 | } 34 | if w.Proxy != "" { 35 | session.Proxies = w.Proxy 36 | } else if config.Conf.Proxy["global"] != "" { 37 | session.Proxies = config.Conf.Proxy["global"] 38 | } 39 | for k, v := range w.Headers { 40 | session.Headers.Set(k, v) 41 | 42 | } 43 | return &Client{ 44 | Session: session, 45 | } 46 | } 47 | 48 | func (c *Client) Do(method, url string, req *url.Request) (*models.Response, error) { 49 | return c.Session.Request(method, url, req) 50 | } 51 | -------------------------------------------------------------------------------- /cmd/server/a.sh: -------------------------------------------------------------------------------- 1 | #交叉编译 2 | #Windows PowerShell 3 | #年月日 4 | $date=$(Get-Date -Format "yyyyMMdd") 5 | $name="ddddhmlistserver" 6 | #win 64 7 | $env:CGO_ENABLED=0 8 | $env:GOOS="windows" 9 | $env:GOARCH="amd64" 10 | go build -o ./bin/$name-$date-win64.exe -ldflags "-s -w " ./app.go 11 | upx -9 ./bin/$name-$date-win64.exe 12 | 13 | #win 32 14 | $env:GOOS="windows" 15 | $env:GOARCH="386" 16 | go build -o ./bin/$name-$date-win32.exe -ldflags "-s -w " ./app.go 17 | upx -9 ./bin/$name-$date-win32.exe 18 | 19 | #linux 64 20 | $env:GOOS="linux" 21 | $env:GOARCH="amd64" 22 | go build -o ./bin/$name-$date-linux64 -ldflags "-s -w " ./app.go 23 | upx -9 ./bin/$name-$date-linux64 24 | 25 | #linux x86 26 | $env:GOOS="linux" 27 | $env:GOARCH="386" 28 | go build -o ./bin/$name-$date-linux32 -ldflags "-s -w " ./app.go 29 | upx -9 ./bin/$name-$date-linux32 30 | #linux arm 31 | $env:GOOS="linux" 32 | $env:GOARCH="arm" 33 | go build -o ./bin/$name-$date-linux-arm -ldflags "-s -w " ./app.go 34 | upx -9 ./bin/$name-$date-linux-arm 35 | 36 | #linux arm64 37 | $env:GOOS="linux" 38 | $env:GOARCH="arm64" 39 | go build -o ./bin/$name-$date-linux-arm64 -ldflags "-s -w " ./app.go 40 | upx -9 ./bin/$name-$date-linux-arm64 41 | 42 | 43 | $env:GOOS="darwin" 44 | $env:GOARCH="amd64" 45 | go build -o ./bin/$name-$date-mac64 -ldflags "-s -w " ./app.go 46 | 47 | 48 | #mac arm 49 | $env:GOOS="darwin" 50 | $env:GOARCH="arm" 51 | go build -o ./bin/$name-$date-mac-arm -ldflags "-s -w " ./app.go 52 | 53 | #mac arm64 54 | $env:GOOS="darwin" 55 | $env:GOARCH="arm64" 56 | go build -o ./bin/$name-$date-mac-arm64 -ldflags "-s -w " ./app.go 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /cmd/client/a.sh: -------------------------------------------------------------------------------- 1 | #交叉编译 2 | #Windows PowerShell 3 | #年月日 4 | $date=$(Get-Date -Format "yyyyMMdd") 5 | $name="ddddhmlistclient" 6 | #win 64 7 | $env:CGO_ENABLED=0 8 | $env:GOOS="windows" 9 | $env:GOARCH="amd64" 10 | go build -o ./bin/$name-$date-win64.exe -ldflags "-s -w " ./client.go 11 | upx -9 ./bin/$name-$date-win64.exe 12 | 13 | #win 32 14 | $env:GOOS="windows" 15 | $env:GOARCH="386" 16 | go build -o ./bin/$name-$date-win32.exe -ldflags "-s -w " ./client.go 17 | upx -9 ./bin/$name-$date-win32.exe 18 | 19 | #linux 64 20 | $env:GOOS="linux" 21 | $env:GOARCH="amd64" 22 | go build -o ./bin/$name-$date-linux64 -ldflags "-s -w " ./client.go 23 | upx -9 ./bin/$name-$date-linux64 24 | 25 | #linux x86 26 | $env:GOOS="linux" 27 | $env:GOARCH="386" 28 | go build -o ./bin/$name-$date-linux32 -ldflags "-s -w " ./client.go 29 | upx -9 ./bin/$name-$date-linux32 30 | #linux arm 31 | $env:GOOS="linux" 32 | $env:GOARCH="arm" 33 | go build -o ./bin/$name-$date-linux-arm -ldflags "-s -w " ./client.go 34 | upx -9 ./bin/$name-$date-linux-arm 35 | 36 | #linux arm64 37 | $env:GOOS="linux" 38 | $env:GOARCH="arm64" 39 | go build -o ./bin/$name-$date-linux-arm64 -ldflags "-s -w " ./client.go 40 | upx -9 ./bin/$name-$date-linux-arm64 41 | 42 | 43 | $env:GOOS="darwin" 44 | $env:GOARCH="amd64" 45 | go build -o ./bin/$name-$date-mac64 -ldflags "-s -w " ./client.go 46 | 47 | 48 | #mac arm 49 | $env:GOOS="darwin" 50 | $env:GOARCH="arm" 51 | go build -o ./bin/$name-$date-mac-arm -ldflags "-s -w " ./client.go 52 | 53 | #mac arm64 54 | $env:GOOS="darwin" 55 | $env:GOARCH="arm64" 56 | go build -o ./bin/$name-$date-mac-arm64 -ldflags "-s -w " ./client.go 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /website/yangshipin/tars/ONATVDetailsVideoSquareListItem.tars: -------------------------------------------------------------------------------- 1 | #include "VideoDetailsResponse.tars" 2 | module Cctv 3 | { 4 | struct NavigationItem 5 | { 6 | 0 require string title; 7 | 1 require string navigationKey; 8 | 2 optional bool selected; 9 | }; 10 | struct ShapeInfo 11 | { 12 | 0 require int type; 13 | 1 optional string color; 14 | }; 15 | struct ONARichTitleItem 16 | { 17 | 0 require TextInfo leftTitleInfo; 18 | 1 require TextInfo rightTitleInfo; 19 | 2 optional ShapeInfo rightShape; 20 | 3 optional int height; 21 | 4 optional string bgColor; 22 | 5 optional Action action; 23 | 6 optional Impression impression; 24 | 7 optional string rightIconUrl; 25 | 8 optional string leftIconUrl; 26 | 9 optional string mainIconUrl; 27 | 10 optional string subBgImageUrl; 28 | }; 29 | struct ONATVDetailsVideoSquareListItem 30 | { 31 | 0 require string dataKey; 32 | 1 require vector videoList; 33 | 2 optional Paging paging; 34 | 3 optional vector navigationItemList; 35 | 4 optional int uiType; 36 | 5 optional ONARichTitleItem title; 37 | 6 optional Impression impression; 38 | 7 optional string playerTitle; 39 | 8 optional int cardOrientation; 40 | 9 optional TextInfo thirdLine; 41 | }; 42 | }; -------------------------------------------------------------------------------- /server/proto/ddddlist.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package server; 3 | option go_package = "../../server"; 4 | import "trpc/proto/trpc_options.proto"; 5 | import "validate/validate.proto"; 6 | service DDDDhm { 7 | rpc DdddList (Request) returns (Response) { option(trpc.alias) = "/api/v1/ddddlist"; } 8 | rpc Hello (HelloRequest) returns (HelloResponse) { option(trpc.alias) = "/api/v1/helloworld"; } 9 | } 10 | message HelloRequest { 11 | string msg = 1; 12 | } 13 | 14 | message HelloResponse { 15 | string msg = 1; 16 | } 17 | message Request { 18 | string url = 1; 19 | string country = 2; 20 | int32 length = 3; 21 | int32 offset = 4; 22 | } 23 | message Response { 24 | int32 code = 1; 25 | string message = 2; 26 | Data video= 3; 27 | } 28 | message Data { 29 | string SeriesId = 1; 30 | string SeriesTitle = 2; 31 | string descr = 3; 32 | repeated Video videoList = 4; 33 | bool isVip = 5; 34 | string coverHzUrl = 6; 35 | string coverVtUrl = 7; 36 | string typeName = 8; 37 | string categoryName = 9; 38 | uint32 vidNum = 10; 39 | uint32 updateNum = 11; 40 | string status = 12; 41 | string createTime = 13; 42 | bool IsSeries = 14; 43 | map extra = 15; 44 | } 45 | message Video { 46 | string EpisodeTitle = 1; 47 | string EpisodeId = 2; 48 | string subTitle = 3; 49 | string coverUrl = 4; 50 | bool isTrailer = 5; 51 | bool isVip = 8; 52 | uint32 duration = 9; 53 | string status = 11; 54 | uint32 episode = 12; 55 | map extra = 13; 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /website/MIGU/mg_test.go: -------------------------------------------------------------------------------- 1 | package MIGU 2 | 3 | import ( 4 | "context" 5 | "github.com/Tontonnow/ddddhmlist/server" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | var ctx = context.WithValue(context.Background(), "requestId", "test") 11 | 12 | func TestGetVideoInfo(t *testing.T) { 13 | type args struct { 14 | ctx context.Context 15 | u string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | want *server.Data 21 | wantErr bool 22 | }{ 23 | { 24 | name: "TestGetVideoInfo", 25 | args: args{ 26 | ctx: ctx, 27 | u: "https://m.miguvideo.com/m/detail/899519544?channelId=10010001005", 28 | }, 29 | want: &server.Data{ 30 | SeriesId: "5105268712", 31 | SeriesTitle: "龙凤店传奇", 32 | }, 33 | wantErr: false, 34 | }, 35 | { 36 | name: "TestGetVideoInfo", 37 | args: args{ 38 | ctx: ctx, 39 | u: "https://www.miguvideo.com/p/detail/887504017", 40 | }, 41 | want: &server.Data{ 42 | SeriesId: "5105231814", 43 | SeriesTitle: "以爱为营", 44 | }, 45 | wantErr: false, 46 | }, 47 | { 48 | name: "TestGetVideoInfo", 49 | args: args{ 50 | ctx: ctx, 51 | u: "https://m.miguvideo.com/m/detail/905514014?channelId=10010001005", 52 | }, 53 | want: &server.Data{ 54 | SeriesId: "5105269347", 55 | SeriesTitle: "饥饿游戏:鸣鸟与蛇之歌", 56 | }, 57 | wantErr: false, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | d, err := GetMateInfo(tt.args.ctx, tt.args.u) 63 | assert.Equal(t, err, 0) 64 | assert.Equal(t, tt.want.SeriesId, d.SeriesId) 65 | assert.Equal(t, tt.want.SeriesTitle, d.SeriesTitle) 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Tontonnow/ddddhmlist/utils" 6 | "github.com/spf13/viper" 7 | "os" 8 | ) 9 | 10 | var ( 11 | Conf = NewConfig() 12 | IsLoad = false 13 | ) 14 | 15 | type Config struct { 16 | Mac string `json:"Mac"` 17 | AndroidId string `json:"AndroidId"` 18 | Proxy map[string]string `json:"Proxy"` 19 | WebConfig map[string]WebConfig `json:"WebConfig"` 20 | } 21 | type WebConfig struct { 22 | Authorization string `json:"Authorization"` 23 | ExpiresIn int `json:"ExpiresIn"` 24 | Proxy string `json:"Proxy"` 25 | Headers map[string]string `json:"Headers"` 26 | Cookie string `json:"Cookie"` 27 | } 28 | 29 | func NewConfig() *Config { 30 | return &Config{ 31 | Mac: "00:00:00:00:00:00", 32 | AndroidId: "0000000000000000", 33 | Proxy: map[string]string{}, 34 | } 35 | } 36 | func updateConfig(key string, value interface{}) { 37 | viper.Set(key, value) 38 | err := viper.WriteConfig() 39 | if err != nil { 40 | fmt.Printf("写入配置文件失败:%s\n", err) 41 | } 42 | } 43 | func InitConfig() { 44 | if IsLoad { 45 | return 46 | } 47 | workDir, err := os.Getwd() 48 | if err != nil { 49 | panic(fmt.Errorf("读取目录失败:%s\n", err)) 50 | } 51 | viper.SetConfigName("config") 52 | viper.SetConfigType("yml") 53 | viper.AddConfigPath(workDir) 54 | viper.AddConfigPath(".") 55 | viper.AddConfigPath("./config") 56 | viper.AddConfigPath(workDir + "/config") 57 | err = viper.ReadInConfig() 58 | if err != nil { 59 | fmt.Printf("读取配置文件失败:%s\n", err) 60 | err = viper.SafeWriteConfigAs("config.yml") 61 | if err != nil { 62 | fmt.Printf("写入配置文件失败:%s\n", err) 63 | } 64 | err = viper.ReadInConfig() 65 | if err != nil { 66 | fmt.Printf("再次读取配置文件失败:%s\n", err) 67 | os.Exit(1) 68 | } 69 | } 70 | err = viper.Unmarshal(Conf) 71 | if err != nil { 72 | fmt.Printf("解析配置文件失败:%s\n", err) 73 | os.Exit(1) 74 | } 75 | if Conf.AndroidId == "" { 76 | Conf.AndroidId = utils.GenerateRandomAndroidId() 77 | } 78 | if Conf.Mac == "" { 79 | Conf.Mac = utils.GenerateRandomMac() 80 | } 81 | IsLoad = true 82 | } 83 | -------------------------------------------------------------------------------- /website/QQTV/proto/trpc_video_detail_list.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "basic_data.proto"; 3 | import "feed_data.proto"; 4 | 5 | package QQTV; 6 | option go_package = "../../QQTV"; 7 | service VideoDetail { 8 | rpc GetDetailPage(DetailPageReq) returns (DetailPageRsp); 9 | rpc GetAlbumInfo(AlbumInfoReq) returns (AlbumInfoRsp); 10 | rpc GetDeatailMore(DetailMoreListReq) returns (DetailMoreListRsp); 11 | rpc GetDetailVideoList(DetailVideoListReq) returns (DetailVideoListRsp); 12 | rpc GetAlbumList(AlbumListReq) returns (AlbumListRsp); 13 | } 14 | 15 | message UserVipInfo { 16 | bool is_vip = 1; 17 | int64 vip_end_time = 2; 18 | } 19 | 20 | message DetailPageReq { 21 | string vid = 1; 22 | string cid = 2; 23 | UserVipInfo user_vip_info = 3; 24 | } 25 | 26 | message DetailPageRsp { 27 | VideoDetailBasicInfo basic_info = 1; 28 | repeated ChannelFeedItem feed_list = 2; 29 | repeated ExperimentInfo experiment_info = 3; 30 | NextPageInfo next_page_info = 4; 31 | repeated DetailTabItem tab_list = 5; 32 | int32 tab_selected_index = 6; 33 | FeedVIPPrBanner vip_banner = 7; 34 | } 35 | 36 | message DetailTabItem { 37 | string tab_id = 1; 38 | string tab_name = 2; 39 | DetailTabType tab_type = 3; 40 | string tab_value = 4; 41 | DetailTabNextPlayModule next_play_module = 5; 42 | } 43 | 44 | message AlbumInfoReq { 45 | string cid = 1; 46 | } 47 | 48 | message AlbumInfoRsp { 49 | AlbumDataInfo album_data_info = 1; 50 | } 51 | 52 | enum DetailTabNextPlayModule { 53 | MODULE_TAB = 0; 54 | MODULE_RECOMMEND = 1; 55 | } 56 | 57 | enum DetailTabType { 58 | TYPE_DETAIL = 0; 59 | TYPE_H5 = 1; 60 | TYPE_CLIPS = 2; 61 | } 62 | message DetailMoreListReq { 63 | string data_key = 1; 64 | string page_context = 2; 65 | } 66 | 67 | message DetailMoreListRsp { 68 | repeated ChannelFeedItem feed_list = 1; 69 | NextPageInfo next_page_info = 2; 70 | } 71 | 72 | message DetailVideoListReq { 73 | string data_key = 1; 74 | string page_context = 2; 75 | } 76 | 77 | message DetailVideoListRsp { 78 | repeated VideoItemData video_list = 1; 79 | NextPageInfo next_page_info = 2; 80 | } 81 | 82 | message AlbumListReq { 83 | string date_key = 1; 84 | string page_context = 2; 85 | } 86 | 87 | message AlbumListRsp { 88 | repeated AlbumDataInfo album_data_info = 1; 89 | NextPageInfo next_page_info = 2; 90 | repeated ExperimentInfo Experiment_info = 3; 91 | } 92 | -------------------------------------------------------------------------------- /website/IQ/iqy_test.go: -------------------------------------------------------------------------------- 1 | package IQ 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/stretchr/testify/assert" 7 | "math/rand" 8 | "testing" 9 | ) 10 | 11 | var ctx = context.WithValue(context.Background(), "requestId", "test") 12 | 13 | func TestGetVid(t *testing.T) { 14 | t.Run("GetVid", func(t *testing.T) { 15 | v, err, c := GetVid("https://www.iqiyi.com/v_22dchdtcba0.html?r_area=recent_popular&r_source=1001&bkt=hp_bkt_02&e=3c7f1fb60e7e64bb39e37b55194f7711&stype=2&vfrm=pcw_home&vfrmblk=pcw_home_hot&vfrmrst=pcw_home_hot_image2") 16 | assert.Nil(t, err) 17 | assert.Equal(t, "22dchdtcba0", v) 18 | assert.False(t, c) 19 | }) 20 | t.Run("GetVid", func(t *testing.T) { 21 | v, err, c := GetVid("https://www.iq.com/album/%E8%8A%B1%E6%BA%AA%E8%AE%B0-2023-1tzhhn9abvp?lang=zh_cn") 22 | assert.Nil(t, err) 23 | assert.Equal(t, "1tzhhn9abvp", v) 24 | assert.False(t, c) 25 | }) 26 | t.Run("GetVid", func(t *testing.T) { 27 | v, err, c := GetVid("https://m.iqiyi.com/m/playShare?shareId=NTMzODkxNTA3MjE3MzQwMA%3D%3D&positiveId=NTMzODkxNTA3MjE3MzQwMA%3D%3D&type=0&rpage=sharepage_new") 28 | assert.Nil(t, err) 29 | assert.Equal(t, "5338915072173400", v) 30 | assert.True(t, c) 31 | }) 32 | } 33 | 34 | func TestGetMateInfo(t *testing.T) { 35 | 36 | t.Run("电影", func(t *testing.T) { 37 | v, err := GetMateInfo(ctx, "https://www.iqiyi.com/v_2704o3ve8qw.html") 38 | assert.Equal(t, err, 0) 39 | assert.Equal(t, "8149704202414500", v.SeriesId) 40 | assert.Equal(t, "养蜂人", v.SeriesTitle) 41 | assert.False(t, v.IsSeries) 42 | fmt.Println(v.Extra) 43 | }) 44 | t.Run("电视剧 动漫 纪录片 儿童 知识", func(t *testing.T) { 45 | v, err := GetMateInfo(ctx, "https://www.iqiyi.com/v_22dchdtcba0.html?r_area=recent_popular&r_source=1001&bkt=hp_bkt_02&e=3c7f1fb60e7e64bb39e37b55194f7711&stype=2&vfrm=pcw_home&vfrmblk=pcw_home_hot&vfrmrst=pcw_home_hot_image2") 46 | assert.Equal(t, err, 0) 47 | assert.Equal(t, "8826732176010901", v.SeriesId) 48 | assert.Equal(t, "乘风踏浪", v.SeriesTitle) 49 | assert.True(t, v.IsSeries) 50 | }) 51 | t.Run("长篇", func(t *testing.T) { 52 | v, err := GetMateInfo(ctx, "https://www.iqiyi.com/v_19rrok4nt0.html") 53 | assert.Equal(t, err, 0) 54 | assert.Equal(t, "202861101", v.SeriesId) 55 | assert.Equal(t, "航海王", v.SeriesTitle) 56 | assert.True(t, v.IsSeries) 57 | for i := 0; i < 5; i++ { 58 | r := rand.Intn(len(v.VideoList)) 59 | assert.Equal(t, v.VideoList[r].Episode, uint32(r+1)) 60 | } 61 | fmt.Println(v.Extra) 62 | }) 63 | t.Run("综艺", func(t *testing.T) { 64 | v, err := GetMateInfo(ctx, "https://www.iqiyi.com/v_19ru98a2w4.html?vfrm=pcw_playpage&vfrmblk=80521_listbox_positive&vfrmrst=0") 65 | assert.Equal(t, err, 0) 66 | assert.Equal(t, "246967201", v.SeriesId) 67 | assert.Equal(t, "坑王驾到第4季", v.SeriesTitle) 68 | assert.True(t, v.IsSeries) 69 | fmt.Println(v.Extra) 70 | }) 71 | 72 | } 73 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Tontonnow/ddddhmlist 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/bitly/go-simplejson v0.5.1 7 | github.com/envoyproxy/protoc-gen-validate v1.0.4 8 | github.com/golang-jwt/jwt/v5 v5.2.1 9 | github.com/google/uuid v1.4.0 10 | github.com/spf13/viper v1.18.2 11 | github.com/stretchr/testify v1.9.0 12 | github.com/wangluozhe/requests v1.2.4 13 | google.golang.org/protobuf v1.33.0 14 | trpc.group/trpc-go/trpc-filter/debuglog v1.0.0 15 | trpc.group/trpc-go/trpc-filter/recovery v1.0.0 16 | trpc.group/trpc-go/trpc-filter/validation v1.0.1 17 | trpc.group/trpc-go/trpc-go v1.0.2 18 | trpc.group/trpc/trpc-protocol/pb/go/trpc v1.0.0 19 | ) 20 | 21 | require ( 22 | github.com/BurntSushi/toml v1.3.2 // indirect 23 | github.com/andybalholm/brotli v1.0.6 // indirect 24 | github.com/cloudflare/circl v1.3.6 // indirect 25 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 26 | github.com/fsnotify/fsnotify v1.7.0 // indirect 27 | github.com/go-playground/form/v4 v4.2.0 // indirect 28 | github.com/golang/snappy v0.0.4 // indirect 29 | github.com/google/flatbuffers v23.5.26+incompatible // indirect 30 | github.com/hashicorp/errwrap v1.1.0 // indirect 31 | github.com/hashicorp/go-multierror v1.1.1 // indirect 32 | github.com/hashicorp/hcl v1.0.0 // indirect 33 | github.com/json-iterator/go v1.1.12 // indirect 34 | github.com/klauspost/compress v1.17.4 // indirect 35 | github.com/lestrrat-go/strftime v1.0.6 // indirect 36 | github.com/magiconair/properties v1.8.7 // indirect 37 | github.com/mitchellh/mapstructure v1.5.0 // indirect 38 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 39 | github.com/modern-go/reflect2 v1.0.2 // indirect 40 | github.com/panjf2000/ants/v2 v2.8.1 // indirect 41 | github.com/pelletier/go-toml/v2 v2.1.0 // indirect 42 | github.com/pkg/errors v0.9.1 // indirect 43 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 44 | github.com/quic-go/quic-go v0.40.1 // indirect 45 | github.com/refraction-networking/utls v1.6.0 // indirect 46 | github.com/rogpeppe/go-internal v1.12.0 // indirect 47 | github.com/sagikazarmark/locafero v0.4.0 // indirect 48 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 49 | github.com/sourcegraph/conc v0.3.0 // indirect 50 | github.com/spf13/afero v1.11.0 // indirect 51 | github.com/spf13/cast v1.6.0 // indirect 52 | github.com/spf13/pflag v1.0.5 // indirect 53 | github.com/subosito/gotenv v1.6.0 // indirect 54 | github.com/valyala/bytebufferpool v1.0.0 // indirect 55 | github.com/valyala/fasthttp v1.48.0 // indirect 56 | github.com/wangluozhe/chttp v0.0.4 // indirect 57 | go.uber.org/atomic v1.11.0 // indirect 58 | go.uber.org/automaxprocs v1.5.3 // indirect 59 | go.uber.org/multierr v1.11.0 // indirect 60 | go.uber.org/zap v1.25.0 // indirect 61 | golang.org/x/crypto v0.18.0 // indirect 62 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 63 | golang.org/x/net v0.20.0 // indirect 64 | golang.org/x/sync v0.5.0 // indirect 65 | golang.org/x/sys v0.16.0 // indirect 66 | golang.org/x/text v0.14.0 // indirect 67 | gopkg.in/ini.v1 v1.67.0 // indirect 68 | gopkg.in/yaml.v3 v3.0.1 // indirect 69 | trpc.group/trpc-go/tnet v1.0.0 // indirect 70 | ) 71 | -------------------------------------------------------------------------------- /trpc_go.yaml: -------------------------------------------------------------------------------- 1 | global: # Global configuration. 2 | namespace: Development # Environment type, either Production or Development. 3 | env_name: test # Environment name for non-production environments. 4 | 5 | server: # Server configuration. 6 | app: DDDDhm # Application name for the business. 7 | server: DdddList # Process server name. 8 | multiplexed: 9 | enable_metrics: true 10 | filter: # List of interceptors for all service handler functions. 11 | - ddddFilter 12 | - SimpleLogFunc 13 | - recovery # Intercept panics from business processing goroutines created by the framework. 14 | - validation 15 | service: # Services provided by the business, can have multiple. 16 | - name: DDDDhm.DdddListService # Route name for the service. 17 | ip: 127.0.0.1 # Service listening IP address, can use placeholder ${ip}. Use either ip or nic, ip takes priority. 18 | # nic: eth0 19 | port: 8002 # Service listening port, can use placeholder ${port}. 20 | network: tcp # Network listening type: tcp or udp. 21 | protocol: trpc # Application layer protocol: trpc or http. 22 | timeout: 1000 # Maximum processing time for requests in milliseconds. 23 | - name: DDDDhm.DdddListServiceHTTP # Route name for the service. 24 | ip: 127.0.0.1 # Service listening IP address, can use placeholder ${ip}. Use either ip or nic, ip takes priority. 25 | # nic: eth0 26 | port: 8001 # Service listening port, can use placeholder ${port}. 27 | network: tcp # Network listening type: tcp or udp. 28 | protocol: http # Application layer protocol: trpc or http. 29 | timeout: 1000 # Maximum processing time for requests in milliseconds. 30 | 31 | 32 | 33 | client: # Backend configuration for client calls. 34 | timeout: 1000 # Maximum processing time for all backends. 35 | namespace: Development # Environment for all backends. 36 | filter: # List of interceptors for all backend function calls. 37 | - validation 38 | service: # Configuration for individual backends. 39 | - name: DDDDhm.DdddListService # Service name for the backend. 40 | namespace: Development # Environment for the backend. 41 | network: tcp # Network type for the backend: tcp or udp (configuration takes priority). 42 | protocol: trpc 43 | target: ip://127.0.0.1:8002 44 | timeout: 1000 45 | plugins: 46 | log: 47 | default: 48 | - writer: console 49 | level: debug 50 | formatter: console 51 | formatter_config: 52 | time_fmt: 2006-01-02 15:04:05 53 | time_key: Time 54 | level_key: Level 55 | name_key: Name 56 | caller_key: Caller 57 | message_key: Message 58 | stacktrace_key: StackTrace 59 | - writer: file 60 | level: info 61 | formatter: json 62 | formatter_config: 63 | time_fmt: 2006-01-02 15:04:05 64 | time_key: Time 65 | level_key: Level 66 | name_key: Name 67 | caller_key: Caller 68 | message_key: Message 69 | stacktrace_key: StackTrace 70 | writer_config: 71 | log_path: ./tmp/log/ 72 | filename: ddddlist.log 73 | write_mode: 2 74 | roll_type: time 75 | max_age: 7 76 | max_backups: 10 77 | compress: false 78 | max_size: 10 -------------------------------------------------------------------------------- /website/yangshipin/tars/RequestCommand.tars: -------------------------------------------------------------------------------- 1 | module Cctv 2 | { 3 | struct BucketConfig 4 | { 5 | 0 require int bucketId; 6 | 1 optional string extra; 7 | }; 8 | struct ExtentData 9 | { 10 | 0 optional int checkFlag; 11 | 1 require byte flagByte; 12 | 2 optional string extra; 13 | 3 optional BucketConfig bucketInfo; 14 | }; 15 | struct Coordinates 16 | { 17 | 0 optional int type; 18 | 1 optional float latitude; 19 | 2 optional float longitude; 20 | 3 optional double accuracy; 21 | }; 22 | struct QUA 23 | { 24 | 0 require string versionName; 25 | 1 require string versionCode; 26 | 2 optional int screenWidth; 27 | 3 optional int screenHeight; 28 | 4 optional int platform; 29 | 5 optional string platformVersion; 30 | 6 optional int markerId; 31 | 7 optional int networkMode; 32 | 8 optional int densityDpi; 33 | 9 optional string channelId; 34 | 10 optional string imei; 35 | 11 optional string imsi; 36 | 12 optional string idfa; 37 | 13 optional string omgId; 38 | 14 optional string extent; 39 | 15 optional ExtentData extentData; 40 | 16 optional string clientKey; 41 | 17 optional string mac; 42 | 18 optional string serverid; 43 | 19 optional Coordinates coordinates; 44 | 20 optional string deviceId; 45 | 21 optional string deviceModel; 46 | 22 optional int deviceType; 47 | 23 optional int mobileISP; 48 | 24 optional int areaMode; 49 | 25 optional int countryCode; 50 | 26 optional int langCode; 51 | 27 optional string appSubVersion; 52 | }; 53 | struct LoginToken 54 | { 55 | 0 require string TokenAppID; 56 | 1 require byte TokenKeyType; 57 | 2 require vector TokenValue; 58 | 3 optional string TokenUin; 59 | 4 optional bool IsMainLogin; 60 | }; 61 | struct ExtentAccount 62 | { 63 | 0 require int type; 64 | 1 require string accountId; 65 | }; 66 | struct SafeInfo 67 | { 68 | 0 optional int type; 69 | 1 optional string SafeKey; 70 | 2 optional vector SafeResult; 71 | }; 72 | struct BusinessExtent 73 | { 74 | 0 optional int launchType; 75 | }; 76 | struct RequestHead 77 | { 78 | 0 require int requestId; 79 | 1 require int cmdId; 80 | 2 optional QUA qua; 81 | 3 optional string appId; 82 | 4 optional string guid; 83 | 5 optional vector token; 84 | 7 optional vector extentAccountList; 85 | 8 optional int oemPlatform; 86 | 9 optional int isSupportDolby; 87 | 10 optional int contentType; 88 | 11 optional SafeInfo safeInfo; 89 | 12 optional BusinessExtent busiExtent; 90 | }; 91 | struct BusinessHead 92 | { 93 | 0 require int type; 94 | 1 optional vector head; 95 | }; 96 | struct RequestCommand 97 | { 98 | 0 require RequestHead head; 99 | 1 optional vector body; 100 | 2 optional BusinessHead businessHead; 101 | }; 102 | }; -------------------------------------------------------------------------------- /website/QQTV/proto/request_base.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "env.proto"; 3 | package QQTV; 4 | option go_package = "../../QQTV"; 5 | message SafeInfo { 6 | int32 type = 1; 7 | string safe_key = 2; 8 | bytes safe_result = 3; 9 | } 10 | 11 | message LoginToken { 12 | string app_id = 1; 13 | int32 type = 2; 14 | string account = 3; 15 | bytes token = 4; 16 | bool is_main_login = 5; 17 | } 18 | 19 | message LocationInfo { 20 | int32 type = 1; 21 | float latitude = 2; 22 | float longitude = 3; 23 | double accuracy = 4; 24 | int32 area_mode = 5; 25 | int32 country_code = 6; 26 | int32 lang_code = 7; 27 | } 28 | 29 | message NetworkInfo { 30 | int32 network_mode = 1; 31 | int32 mobile_isp = 2; 32 | string ip = 3; 33 | string bss_id = 4; 34 | string eth_mac = 5; 35 | string wifi_mac = 6; 36 | string ipv4 = 7; 37 | string ipv6 = 8; 38 | int32 mcc = 9; 39 | } 40 | 41 | message VersionInfo { 42 | string version_name = 1; 43 | string version_code = 2; 44 | int32 platform = 3; 45 | string platform_version = 4; 46 | string channel_id = 6; 47 | string app_id = 7; 48 | int32 vnview_pkg_version = 8; 49 | } 50 | 51 | message PortraitInfo { 52 | string key = 1; 53 | repeated string value_info = 2; 54 | } 55 | 56 | message DeviceInfo { 57 | int32 screen_width = 1; 58 | int32 screen_height = 2; 59 | int32 density_dpi = 3; 60 | string imei = 4; 61 | string imsi = 5; 62 | string idfa = 6; 63 | string omg_id = 7; 64 | string device_id = 8; 65 | string device_model = 9; 66 | int32 device_type = 10; 67 | string guid = 11; 68 | UISizeType max_uiSize = 12; 69 | UISizeType current_window_uiSize = 13; 70 | string manufacturer = 14; 71 | string qimei = 15; 72 | string localIdfa = 16; 73 | } 74 | 75 | message FlagInfo { 76 | int32 check_flag = 1; 77 | int32 debug_flag = 2; 78 | int32 auto_retry_flag = 3; 79 | int32 need_retry_flag = 4; 80 | } 81 | 82 | message UserStatusInfo { 83 | string session_code = 1; 84 | int32 special_user = 2; 85 | int64 timestamp = 30; 86 | string user_status_key = 3; 87 | int32 dye_flag = 4; 88 | } 89 | 90 | message OttInfo { 91 | string licence = 1; 92 | string qua = 2; 93 | string qas = 3; 94 | } 95 | 96 | message RequestHead { 97 | int32 request_id = 1; 98 | string callee = 2; 99 | string func = 3; 100 | SafeInfo safe_info = 4; 101 | repeated LoginToken login_token = 5; 102 | DeviceInfo device_info = 6; 103 | VersionInfo version_info = 7; 104 | NetworkInfo network_info = 8; 105 | LocationInfo location_info = 9; 106 | FlagInfo flag_info = 10; 107 | BucketInfo bucket_info = 11; 108 | repeated PortraitInfo portrait_info = 12; 109 | string unique_id = 13; 110 | UserStatusInfo user_status_info = 14; 111 | map extra_request_head = 15; 112 | map cookie = 16; 113 | OttInfo ott_info = 17; 114 | EnvInfo env_info = 18; 115 | } 116 | 117 | message ResponseHead { 118 | int32 request_id = 1; 119 | string callee = 2; 120 | string func = 3; 121 | int32 err_code = 4; 122 | SafeInfo safe_info = 5; 123 | FlagInfo flag_info = 6; 124 | repeated PortraitInfo portrait_info = 7; 125 | UserStatusInfo user_status_info = 8; 126 | ServerReportInfo server_report_info = 9; 127 | } 128 | 129 | message ServerReportInfo { 130 | int64 server_rsp_time = 1; 131 | repeated string discontent_reason = 2; 132 | } 133 | 134 | message BucketInfo { 135 | int32 bucket_id = 1; 136 | string extra = 2; 137 | } 138 | 139 | enum UISizeType { 140 | UISizeType_REGULAR = 0; 141 | UISizeType_LARGE = 1; 142 | UISizeType_HUGE = 2; 143 | UISizeType_MAXIMIZE = 3; 144 | UISizeType_XMAXIMIZE = 4; 145 | } 146 | -------------------------------------------------------------------------------- /website/yangshipin/ysp.go: -------------------------------------------------------------------------------- 1 | package yangshipin 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Tontonnow/ddddhmlist/config" 7 | proto2 "github.com/Tontonnow/ddddhmlist/server" 8 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 9 | "github.com/wangluozhe/requests/url" 10 | "google.golang.org/protobuf/proto" 11 | "reflect" 12 | "strconv" 13 | "strings" 14 | "time" 15 | "trpc.group/trpc-go/trpc-go/log" 16 | ) 17 | 18 | var ( 19 | web = "ysp" 20 | pageModel = "https://capi.yangshipin.cn/api/oms/pc/page/PG00000004" 21 | playInfo = "https://csapi.yangshipin.cn/voapi/omsot/album/playinfo" 22 | yspEpg = "https://capi.yangshipin.cn/api/yspepg/program/%s/%s" 23 | headers = map[string]string{ 24 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0", 25 | } 26 | Headers = url.ParseHeaders(headers) 27 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 28 | ) 29 | 30 | func doRequest(u string, respProto proto.Message, req *url.Request) error { 31 | if req == nil { 32 | req = url.NewRequest() 33 | } 34 | if req.Headers == nil { 35 | req.Headers = Headers 36 | } 37 | r, err := client.Do("get", u, req) 38 | if err != nil { 39 | return fmt.Errorf("request failed: %w", err) 40 | } 41 | 42 | err = proto.Unmarshal(r.Content, respProto) 43 | if err != nil { 44 | return fmt.Errorf("unmarshal failed: %w", err) 45 | } 46 | 47 | respCode := reflect.ValueOf(respProto).Elem().FieldByName("Code").Uint() // Assumes the response has a Code field 48 | if respCode != 200 { 49 | respMessage := reflect.ValueOf(respProto).Elem().FieldByName("Message").String() // Assumes the response has a Message field 50 | return fmt.Errorf("error response: %d %s", respCode, respMessage) 51 | } 52 | 53 | return nil 54 | } 55 | 56 | func GetEpg(pid string, date ...string) []*CnYangshipinOmstvCommonProtoProgramModel_Program { 57 | if len(date) == 0 { 58 | date = append(date, time.Now().Format("20060102")) 59 | } 60 | u := fmt.Sprintf(yspEpg, pid, date[0]) 61 | 62 | epg := &CnYangshipinOmstvCommonProtoEpgProgramModel_Response{} 63 | if err := doRequest(u, epg, nil); err != nil { 64 | log.Error(err) 65 | return nil 66 | } 67 | 68 | return epg.DataList 69 | } 70 | 71 | func GetPlayInfo(cid string) *CnYangshipinOmsCommonProtoAlbumPlayInfoModel_Data { 72 | tm := time.Now().Unix() / 5 73 | params := map[string]string{ 74 | "cid": cid, 75 | "ts": strconv.FormatInt(tm, 10), 76 | } 77 | 78 | req := url.NewRequest() 79 | req.Params = url.ParseParams(params) 80 | playInfoResp := &CnYangshipinOmsCommonProtoAlbumPlayInfoModel_Response{} 81 | if err := doRequest(playInfo, playInfoResp, req); err != nil { 82 | log.Error(err) 83 | return nil 84 | } 85 | 86 | return playInfoResp.Data 87 | } 88 | func GetEpgId() *CnYangshipinOmsCommonProtoPageModel_Data { 89 | pageModelResp := &CnYangshipinOmsCommonProtoPageModel_Response{} 90 | if err := doRequest(pageModel, pageModelResp, nil); err != nil { 91 | log.Error(err) 92 | return nil 93 | } 94 | 95 | return pageModelResp.Data 96 | } 97 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *proto2.Data, code int) { 98 | var err error 99 | requestId := ctx.Value("requestId").(string) 100 | log.Debugf("site: %s, start GetMateInfo", web) 101 | r = &proto2.Data{} 102 | u := strings.Replace(sharerUrl, "/#/", "/o/", 1) 103 | ud, err := url.Parse(u) 104 | if err != nil { 105 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 106 | code = 2 107 | return 108 | } 109 | cid := ud.Params.Get("cid") 110 | if cid == "" { 111 | err = fmt.Errorf("cid is empty") 112 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 113 | code = 2 114 | return 115 | } 116 | playInfo := GetPlayInfo(cid) 117 | if playInfo == nil { 118 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 119 | code = 1 120 | return 121 | } 122 | r.SeriesTitle = playInfo.Title 123 | r.SeriesId = playInfo.Cid 124 | r.IsVip = playInfo.IsVip 125 | for _, v := range playInfo.VideoList { 126 | r.VideoList = append(r.VideoList, &proto2.Video{ 127 | EpisodeId: v.Vid, 128 | EpisodeTitle: v.Title, 129 | Episode: v.Episode, 130 | IsVip: v.IsVip, 131 | }) 132 | } 133 | return 134 | } 135 | -------------------------------------------------------------------------------- /website/QQTV/TXTV.go: -------------------------------------------------------------------------------- 1 | package QQTV 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "strconv" 12 | "strings" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | var ( 17 | web = "TX" 18 | getPlayInfo = "https://tv.aiseet.atianqi.com/i-tvbin/qtv_video/get_play_info?cid=%s&Q-UA=PT%%3DTVMORE%%26CHID%%3D10009%%26VN%%3D1.9.0" 19 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 20 | ) 21 | 22 | type playInfo struct { 23 | Result struct { 24 | Ret int `json:"ret"` 25 | Msg string `json:"msg"` 26 | CostTime int `json:"cost_time"` 27 | Code int `json:"code"` 28 | } `json:"result"` 29 | Data struct { 30 | CID string `json:"c_id"` 31 | CType int `json:"c_type"` 32 | CTitle string `json:"c_title"` 33 | CSTitle string `json:"c_s_title"` 34 | CHorizontalPic string `json:"c_horizontal_pic"` 35 | CVerticalPic string `json:"c_vertical_pic"` 36 | CPayStatus int `json:"c_pay_status"` 37 | Imgtag string `json:"imgtag"` 38 | Videos []struct { 39 | VID string `json:"v_id"` 40 | VType int `json:"v_type"` 41 | VTitle string `json:"v_title"` 42 | VSTitle string `json:"v_s_title"` 43 | IsTrailer int `json:"is_trailer"` 44 | HeadTime int `json:"head_time"` 45 | TailTime int `json:"tail_time"` 46 | PlayStatus int `json:"play_status"` 47 | Tips string `json:"tips"` 48 | VPayStatus int `json:"v_pay_status"` 49 | UhdFlag int `json:"uhd_flag"` 50 | Pictures map[string]string `json:"pictures"` 51 | NewPayStatus int `json:"new_pay_status"` 52 | SquareTags any `json:"square_tags"` 53 | } `json:"videos"` 54 | CopyrightStatus int `json:"copyright_status"` 55 | Tips string `json:"tips"` 56 | QrcodeURL string `json:"qrcode_url"` 57 | UhdFlag int `json:"uhd_flag"` 58 | Paid int `json:"paid"` 59 | VideoUIInfo struct { 60 | VideoUIType int `json:"videoUIType"` 61 | VideoDataListType int `json:"videoDataListType"` 62 | FreshPage bool `json:"freshPage"` 63 | } `json:"videoUIInfo"` 64 | } `json:"data"` 65 | } 66 | 67 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 68 | var err error 69 | requestId := ctx.Value("requestId").(string) 70 | log.Debugf("site: %s, start GetMateInfo", web) 71 | r = &server.Data{} 72 | cid := ExtractCid(sharerUrl) 73 | if cid == "" { 74 | err = fmt.Errorf("url is invalid") 75 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 76 | code = 2 77 | return 78 | } 79 | getPlayInfoUrl := fmt.Sprintf(getPlayInfo, cid) 80 | res, err := client.Do("get", getPlayInfoUrl, nil) 81 | if err != nil { 82 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 83 | code = 1 84 | return 85 | } 86 | var data playInfo 87 | err = json.Unmarshal(res.Content, &data) 88 | if err != nil { 89 | return 90 | } 91 | if data.Result.Ret != 0 { 92 | err = fmt.Errorf(data.Result.Msg) 93 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 94 | code = 1 95 | return 96 | } 97 | r.SeriesTitle = data.Data.CTitle 98 | r.SeriesId = data.Data.CID 99 | for _, v := range data.Data.Videos { 100 | r.VideoList = append(r.VideoList, &server.Video{ 101 | EpisodeId: v.VID, 102 | EpisodeTitle: v.VTitle + " " + v.VSTitle, 103 | IsTrailer: v.IsTrailer == 1, 104 | Extra: map[string]string{ 105 | "uhd_flag": strconv.Itoa(v.UhdFlag), 106 | }, 107 | }) 108 | } 109 | return 110 | } 111 | func ExtractCid(u string) (cid string) { 112 | ud, err := url.Parse(u) 113 | if err != nil { 114 | log.Error("QQTV", "ExtractCid", err) 115 | return 116 | } 117 | cid = ud.Params.Get("cid") 118 | if cid == "" { 119 | ids := strings.Split(ud.Path, "/") 120 | if len(ids) < 4 { 121 | return 122 | } 123 | cid = ids[3] 124 | cid = strings.Split(cid, ".")[0] 125 | } 126 | return 127 | } 128 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /server/trpc/server.go: -------------------------------------------------------------------------------- 1 | package trpc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Tontonnow/ddddhmlist/server" 7 | "github.com/Tontonnow/ddddhmlist/website/Bilibili" 8 | "github.com/Tontonnow/ddddhmlist/website/Friday" 9 | "github.com/Tontonnow/ddddhmlist/website/Hami" 10 | "github.com/Tontonnow/ddddhmlist/website/IQ" 11 | "github.com/Tontonnow/ddddhmlist/website/KKTV" 12 | "github.com/Tontonnow/ddddhmlist/website/LETV" 13 | "github.com/Tontonnow/ddddhmlist/website/Litv" 14 | "github.com/Tontonnow/ddddhmlist/website/MGTV" 15 | "github.com/Tontonnow/ddddhmlist/website/MIGU" 16 | "github.com/Tontonnow/ddddhmlist/website/MyVideo" 17 | "github.com/Tontonnow/ddddhmlist/website/MytvSuper" 18 | "github.com/Tontonnow/ddddhmlist/website/QQTV" 19 | "github.com/Tontonnow/ddddhmlist/website/VIKI" 20 | "github.com/Tontonnow/ddddhmlist/website/VIU" 21 | "github.com/Tontonnow/ddddhmlist/website/XiGUA" 22 | "github.com/Tontonnow/ddddhmlist/website/YOUKUTV" 23 | "github.com/Tontonnow/ddddhmlist/website/yangshipin" 24 | "github.com/google/uuid" 25 | "runtime/debug" 26 | "strings" 27 | "trpc.group/trpc-go/trpc-filter/debuglog" 28 | _ "trpc.group/trpc-go/trpc-filter/recovery" 29 | _ "trpc.group/trpc-go/trpc-filter/validation" 30 | "trpc.group/trpc-go/trpc-go" 31 | "trpc.group/trpc-go/trpc-go/filter" 32 | trpcHttp "trpc.group/trpc-go/trpc-go/http" 33 | "trpc.group/trpc-go/trpc-go/log" 34 | ) 35 | 36 | var ErrCode = map[int]string{ 37 | 0: "Success", 38 | 1: "内部错误,请排查日志,任务ID%s", 39 | 2: "链接格式可能有误,任务ID%s", 40 | 3: "地区限制,服务器无对应地区代理,任务ID%s", 41 | } 42 | 43 | type DdddListServiceImpl struct { 44 | UnimplementedDDDDhm 45 | } 46 | 47 | func (s *DdddListServiceImpl) DdddList(ctx context.Context, req *server.Request) (*server.Response, error) { 48 | var rsp = &server.Response{ 49 | Code: 0, 50 | Message: ErrCode[0], 51 | } 52 | log.Info("DdddList", "requestId", ctx.Value("requestId")) 53 | data, code := GetMateInfo(ctx, req) 54 | if code != 0 { 55 | rsp.Code = int32(code) 56 | rsp.Message = fmt.Sprintf(ErrCode[code], ctx.Value("requestId")) 57 | return rsp, nil 58 | } 59 | rsp.Video = data 60 | return rsp, nil 61 | 62 | } 63 | func (s *DdddListServiceImpl) Hello(ctx context.Context, req *server.HelloRequest) (*server.HelloResponse, error) { 64 | rsp := &server.HelloResponse{ 65 | Msg: req.GetMsg(), 66 | } 67 | return rsp, nil 68 | } 69 | func GetMateInfo(ctx context.Context, req *server.Request) (r *server.Data, code int) { 70 | url := req.Url 71 | var f func(ctx context.Context, sharerUrl string) (r *server.Data, code int) 72 | r = &server.Data{} 73 | if url == "" { 74 | code = 2 75 | return 76 | } 77 | defer func() { 78 | if err := recover(); err != nil { 79 | stack := string(debug.Stack()) 80 | log.Error("GetMateInfo", "err", err, "stack", stack) 81 | code = 1 82 | return 83 | } 84 | }() 85 | if strings.Contains(url, "friday") { 86 | f = Friday.GetMateInfo 87 | } else if strings.Contains(url, "hamivideo") { 88 | f = Hami.GetMateInfo 89 | } else if strings.Contains(url, ".iq.") { 90 | f = IQ.GetIqMateInfo 91 | } else if strings.Contains(url, ".iqiyi.") { 92 | f = IQ.GetMateInfo 93 | } else if strings.Contains(url, "kktv.me") { 94 | f = KKTV.GetMateInfo 95 | } else if strings.Contains(url, ".le.") { 96 | f = LETV.GetMateInfo 97 | } else if strings.Contains(url, ".litv.") { 98 | f = Litv.GetMateInfo 99 | } else if strings.Contains(url, ".mgtv.") { 100 | f = MGTV.GetMateInfo 101 | } else if strings.Contains(url, "miguvideo") { 102 | f = MIGU.GetMateInfo 103 | } else if strings.Contains(url, ".mytvsuper.") { 104 | f = MytvSuper.GetMateInfo 105 | } else if strings.Contains(url, "myvideo.net.tw") { 106 | f = MyVideo.GetMateInfo 107 | } else if strings.Contains(url, "v.qq.com") { 108 | f = QQTV.GetMateInfo 109 | } else if strings.Contains(url, "wetv.") { 110 | f = QQTV.GetWeTvMateInfo 111 | } else if strings.Contains(url, ".viki.") { 112 | f = VIKI.GetMateInfo 113 | } else if strings.Contains(url, ".viu.") { 114 | f = Viu.GetMateInfo 115 | } else if strings.Contains(url, "wetv.") { 116 | f = QQTV.GetWeTvMateInfo 117 | } else if strings.Contains(url, ".ixigua.") || strings.Contains(url, "lvdetail") { 118 | f = XiGUA.GetMateInfo 119 | } else if strings.Contains(url, "yangshipin") { 120 | f = yangshipin.GetMateInfo 121 | } else if strings.Contains(url, "youku.com") || strings.Contains(url, "youku.tv") { 122 | f = YOUKUTV.GetMateInfo 123 | } else if strings.Contains(url, ".bilibili.") { 124 | f = Bilibili.GetMateInfo 125 | } else { 126 | return r, 2 127 | } 128 | return f(ctx, url) 129 | } 130 | func Filter(ctx context.Context, req interface{}, next filter.ServerHandleFunc) (interface{}, error) { 131 | Head := trpcHttp.Head(ctx) 132 | requestId := "" 133 | if Head != nil { 134 | requestId = Head.Request.Header.Get("X-Request-Id") 135 | } 136 | if requestId == "" || strings.Count(requestId, "-") != 4 { 137 | requestId = uuid.New().String() 138 | } 139 | ctx = context.WithValue(ctx, "requestId", requestId) 140 | trpc.SetMetaData(ctx, "requestId", []byte(requestId)) 141 | rsp, err := next(ctx, req) 142 | return rsp, err 143 | } 144 | 145 | func Init() { 146 | filter.Register("ddddFilter", Filter, nil) 147 | LogFunc := func(ctx context.Context, req, rsp interface{}) string { 148 | return fmt.Sprintf(", requestId:%s", ctx.Value("requestId")) 149 | } 150 | var debuglogOption []debuglog.Option 151 | debuglogOption = append(debuglogOption, debuglog.WithLogFunc(LogFunc)) 152 | debuglogOption = append(debuglogOption, debuglog.WithEnableColor(true)) 153 | filter.Register("SimpleLogFunc", 154 | debuglog.ServerFilter(debuglogOption...), 155 | debuglog.ClientFilter(debuglogOption...)) 156 | s := trpc.NewServer() 157 | RegisterDDDDhmService(s.Service("DDDDhm.DdddListService"), 158 | &DdddListServiceImpl{}) 159 | RegisterDDDDhmService(s.Service("DDDDhm.DdddListServiceHTTP"), 160 | &DdddListServiceImpl{}) 161 | if err := s.Serve(); err != nil { 162 | log.Fatal(err) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /server/trpc/ddddlist.trpc.go: -------------------------------------------------------------------------------- 1 | // Code generated by trpc-go/trpc-cmdline v1.0.6. DO NOT EDIT. 2 | // source: ddddlist.proto 3 | 4 | package trpc 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "fmt" 10 | server2 "github.com/Tontonnow/ddddhmlist/server" 11 | 12 | _ "trpc.group/trpc-go/trpc-go" 13 | "trpc.group/trpc-go/trpc-go/client" 14 | "trpc.group/trpc-go/trpc-go/codec" 15 | _ "trpc.group/trpc-go/trpc-go/http" 16 | "trpc.group/trpc-go/trpc-go/server" 17 | ) 18 | 19 | // START ======================================= Server Service Definition ======================================= START 20 | 21 | // DDDDhmService defines service. 22 | type DDDDhmService interface { 23 | DdddList(ctx context.Context, req *server2.Request) (*server2.Response, error) 24 | 25 | Hello(ctx context.Context, req *server2.HelloRequest) (*server2.HelloResponse, error) 26 | } 27 | 28 | func DDDDhmService_DdddList_Handler(svr interface{}, ctx context.Context, f server.FilterFunc) (interface{}, error) { 29 | req := &server2.Request{} 30 | filters, err := f(req) 31 | if err != nil { 32 | return nil, err 33 | } 34 | handleFunc := func(ctx context.Context, reqbody interface{}) (interface{}, error) { 35 | return svr.(DDDDhmService).DdddList(ctx, reqbody.(*server2.Request)) 36 | } 37 | 38 | var rsp interface{} 39 | rsp, err = filters.Filter(ctx, req, handleFunc) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return rsp, nil 44 | } 45 | 46 | func DDDDhmService_Hello_Handler(svr interface{}, ctx context.Context, f server.FilterFunc) (interface{}, error) { 47 | req := &server2.HelloRequest{} 48 | filters, err := f(req) 49 | if err != nil { 50 | return nil, err 51 | } 52 | handleFunc := func(ctx context.Context, reqbody interface{}) (interface{}, error) { 53 | return svr.(DDDDhmService).Hello(ctx, reqbody.(*server2.HelloRequest)) 54 | } 55 | 56 | var rsp interface{} 57 | rsp, err = filters.Filter(ctx, req, handleFunc) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return rsp, nil 62 | } 63 | 64 | // DDDDhmServer_ServiceDesc descriptor for server.RegisterService. 65 | var DDDDhmServer_ServiceDesc = server.ServiceDesc{ 66 | ServiceName: "server.DDDDhm", 67 | HandlerType: ((*DDDDhmService)(nil)), 68 | Methods: []server.Method{ 69 | { 70 | Name: "/api/v1/ddddlist", 71 | Func: DDDDhmService_DdddList_Handler, 72 | }, 73 | { 74 | Name: "/api/v1/helloworld", 75 | Func: DDDDhmService_Hello_Handler, 76 | }, 77 | { 78 | Name: "/server.DDDDhm/DdddList", 79 | Func: DDDDhmService_DdddList_Handler, 80 | }, 81 | { 82 | Name: "/server.DDDDhm/Hello", 83 | Func: DDDDhmService_Hello_Handler, 84 | }, 85 | }, 86 | } 87 | 88 | // RegisterDDDDhmService registers service. 89 | func RegisterDDDDhmService(s server.Service, svr DDDDhmService) { 90 | if err := s.Register(&DDDDhmServer_ServiceDesc, svr); err != nil { 91 | panic(fmt.Sprintf("DDDDhm register error:%v", err)) 92 | } 93 | } 94 | 95 | // START --------------------------------- Default Unimplemented Server Service --------------------------------- START 96 | 97 | type UnimplementedDDDDhm struct{} 98 | 99 | func (s *UnimplementedDDDDhm) DdddList(ctx context.Context, req *server2.Request) (*server2.Response, error) { 100 | return nil, errors.New("rpc DdddList of service DDDDhm is not implemented") 101 | } 102 | func (s *UnimplementedDDDDhm) Hello(ctx context.Context, req *server2.HelloRequest) (*server2.HelloResponse, error) { 103 | return nil, errors.New("rpc Hello of service DDDDhm is not implemented") 104 | } 105 | 106 | // END --------------------------------- Default Unimplemented Server Service --------------------------------- END 107 | 108 | // END ======================================= Server Service Definition ======================================= END 109 | 110 | // START ======================================= Client Service Definition ======================================= START 111 | 112 | // DDDDhmClientProxy defines service client proxy 113 | type DDDDhmClientProxy interface { 114 | DdddList(ctx context.Context, req *server2.Request, opts ...client.Option) (rsp *server2.Response, err error) 115 | 116 | Hello(ctx context.Context, req *server2.HelloRequest, opts ...client.Option) (rsp *server2.HelloResponse, err error) 117 | } 118 | 119 | type DDDDhmClientProxyImpl struct { 120 | client client.Client 121 | opts []client.Option 122 | } 123 | 124 | var NewDDDDhmClientProxy = func(opts ...client.Option) DDDDhmClientProxy { 125 | return &DDDDhmClientProxyImpl{client: client.DefaultClient, opts: opts} 126 | } 127 | 128 | func (c *DDDDhmClientProxyImpl) DdddList(ctx context.Context, req *server2.Request, opts ...client.Option) (*server2.Response, error) { 129 | ctx, msg := codec.WithCloneMessage(ctx) 130 | defer codec.PutBackMessage(msg) 131 | msg.WithClientRPCName("/api/v1/ddddlist") 132 | msg.WithCalleeServiceName(DDDDhmServer_ServiceDesc.ServiceName) 133 | msg.WithCalleeApp("") 134 | msg.WithCalleeServer("") 135 | msg.WithCalleeService("DDDDhm") 136 | msg.WithCalleeMethod("DdddList") 137 | msg.WithSerializationType(codec.SerializationTypePB) 138 | callopts := make([]client.Option, 0, len(c.opts)+len(opts)) 139 | callopts = append(callopts, c.opts...) 140 | callopts = append(callopts, opts...) 141 | rsp := &server2.Response{} 142 | if err := c.client.Invoke(ctx, req, rsp, callopts...); err != nil { 143 | return nil, err 144 | } 145 | return rsp, nil 146 | } 147 | 148 | func (c *DDDDhmClientProxyImpl) Hello(ctx context.Context, req *server2.HelloRequest, opts ...client.Option) (*server2.HelloResponse, error) { 149 | ctx, msg := codec.WithCloneMessage(ctx) 150 | defer codec.PutBackMessage(msg) 151 | msg.WithClientRPCName("/api/v1/helloworld") 152 | msg.WithCalleeServiceName(DDDDhmServer_ServiceDesc.ServiceName) 153 | msg.WithCalleeApp("") 154 | msg.WithCalleeServer("") 155 | msg.WithCalleeService("DDDDhm") 156 | msg.WithCalleeMethod("Hello") 157 | msg.WithSerializationType(codec.SerializationTypePB) 158 | callopts := make([]client.Option, 0, len(c.opts)+len(opts)) 159 | callopts = append(callopts, c.opts...) 160 | callopts = append(callopts, opts...) 161 | rsp := &server2.HelloResponse{} 162 | if err := c.client.Invoke(ctx, req, rsp, callopts...); err != nil { 163 | return nil, err 164 | } 165 | return rsp, nil 166 | } 167 | 168 | // END ======================================= Client Service Definition ======================================= END 169 | -------------------------------------------------------------------------------- /website/LETV/letv.go: -------------------------------------------------------------------------------- 1 | package LETV 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "regexp" 12 | "strconv" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | var ( 17 | web = "letv" 18 | headers = map[string]string{ 19 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", 20 | "referer": "https://www.le.com", 21 | } 22 | ApiList = "https://d-api-m.le.com/card/dynamic?platform=pc&callback=&vid=%s&cid=%s&pagesize=100&type=episode&isvip=1&page=1" 23 | ApiVideoInfo = "https://www.le.com/service/getVideoInfo/" 24 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 25 | ) 26 | 27 | type VideoInfo struct { 28 | Status string `json:"status"` 29 | Data struct { 30 | IsPay int `json:"isPay"` 31 | Video struct { 32 | Vid string `json:"vid"` 33 | Pid int `json:"pid"` 34 | Cid string `json:"cid"` 35 | Title string `json:"title"` 36 | Duration string `json:"duration"` 37 | VideotypeName string `json:"videotypeName"` 38 | Description string `json:"description"` 39 | CreateTime string `json:"createTime"` 40 | VideoFrom string `json:"video_from"` 41 | VidPlayCount int `json:"vid_Play_Count"` 42 | } `json:"video"` 43 | Album struct { 44 | Title string `json:"title"` 45 | Description string `json:"description"` 46 | PidPlayCount int `json:"pid_Play_Count"` 47 | PlistScore string `json:"plist_score"` 48 | VarietyShow int `json:"varietyShow"` 49 | } `json:"album"` 50 | } `json:"data"` 51 | } 52 | 53 | type ListInfo struct { 54 | Code string `json:"code"` 55 | Data struct { 56 | Position int `json:"position"` 57 | Location int `json:"location"` 58 | Periodpoint []interface{} `json:"periodpoint"` 59 | Otherlist []interface{} `json:"otherlist"` 60 | Relalbum []interface{} `json:"relalbum"` 61 | Relvideo []interface{} `json:"relvideo"` 62 | Episode struct { 63 | Cnt int `json:"cnt"` 64 | IsEnd int `json:"isEnd"` 65 | Now int `json:"now"` 66 | Allcnt int `json:"allcnt"` 67 | Upinfo string `json:"upinfo"` 68 | Yugao []interface{} `json:"yugao"` 69 | Currentpage int `json:"currentpage"` 70 | Videolist []struct { 71 | Duration string `json:"duration"` 72 | Episode string `json:"episode"` 73 | Guest interface{} `json:"guest"` 74 | IsFirstLook int `json:"isFirstLook"` 75 | Key int `json:"key"` 76 | PayPlatforms interface{} `json:"payPlatforms"` 77 | Pic string `json:"pic"` 78 | ReleaseDate string `json:"releaseDate"` 79 | Singer []interface{} `json:"singer"` 80 | Title string `json:"title"` 81 | Url string `json:"url"` 82 | Vid int `json:"vid"` 83 | VideoType int `json:"videoType"` 84 | SubTitle string `json:"subTitle"` 85 | WatchingFocus []interface{} `json:"watchingFocus"` 86 | Ispay int `json:"ispay"` 87 | Nextvid interface{} `json:"nextvid"` 88 | } `json:"videolist"` 89 | } `json:"episode"` 90 | Period []interface{} `json:"period"` 91 | List []interface{} `json:"list"` 92 | } `json:"data"` 93 | Msg string `json:"msg"` 94 | } 95 | 96 | func GetVideoInfo(vid string) (info VideoInfo, err error) { 97 | req := url.NewRequest() 98 | req.Headers = url.ParseHeaders(headers) 99 | u := ApiVideoInfo + vid 100 | rsp, err := client.Do("GET", u, req) 101 | if err != nil { 102 | return 103 | } 104 | data, err := rsp.Json() 105 | if err != nil { 106 | return 107 | } 108 | switch data["data"].(type) { 109 | case string: 110 | err = fmt.Errorf(data["data"].(string)) 111 | return 112 | default: 113 | err = json.Unmarshal(rsp.Content, &info) 114 | if err != nil { 115 | return 116 | } 117 | } 118 | return 119 | } 120 | func GetListInfo(vid, cid string) (info ListInfo, err error) { 121 | req := url.NewRequest() 122 | req.Headers = url.ParseHeaders(headers) 123 | u := fmt.Sprintf(ApiList, vid, cid) 124 | rsp, err := client.Do("GET", u, req) 125 | if err != nil { 126 | return 127 | } 128 | err = json.Unmarshal(rsp.Content, &info) 129 | return 130 | } 131 | func GetVId(url string) string { 132 | re := regexp.MustCompile(`/(\d+).`) 133 | productId := re.FindStringSubmatch(url) 134 | if len(productId) <= 1 { 135 | return "" 136 | } 137 | return productId[1] 138 | } 139 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 140 | var ( 141 | err error 142 | ) 143 | r = &server.Data{} 144 | requestId := ctx.Value("requestId").(string) 145 | log.Debugf("site: %s, start GetMateInfo", web) 146 | vid := GetVId(sharerUrl) 147 | if vid == "" { 148 | code = 2 149 | err = fmt.Errorf("Invalid URL") 150 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 151 | return 152 | } 153 | info, err := GetVideoInfo(vid) 154 | if err != nil { 155 | code = 1 156 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 157 | return 158 | } 159 | r.SeriesTitle = info.Data.Album.Title 160 | r.SeriesId = strconv.Itoa(info.Data.Video.Pid) 161 | r.IsVip = info.Data.IsPay == 1 162 | r.IsSeries = false 163 | if info.Data.Video.Cid != "1" { 164 | r.IsSeries = true 165 | vid = info.Data.Video.Vid 166 | cid := info.Data.Video.Cid 167 | var listInfo ListInfo 168 | listInfo, err = GetListInfo(vid, cid) 169 | if err != nil { 170 | code = 1 171 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 172 | return 173 | } 174 | for _, v := range listInfo.Data.Episode.Videolist { 175 | r.VideoList = append(r.VideoList, &server.Video{ 176 | EpisodeTitle: v.Title, 177 | EpisodeId: strconv.Itoa(v.Vid), 178 | Episode: uint32(v.Key), 179 | IsVip: v.Ispay == 1, 180 | }) 181 | } 182 | 183 | } 184 | return 185 | } 186 | -------------------------------------------------------------------------------- /website/QQTV/env.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.31.0 4 | // protoc v3.21.4 5 | // source: env.proto 6 | 7 | package QQTV 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type EnvInfo struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | User string `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` 30 | Detail string `protobuf:"bytes,3,opt,name=detail,proto3" json:"detail,omitempty"` 31 | Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 32 | } 33 | 34 | func (x *EnvInfo) Reset() { 35 | *x = EnvInfo{} 36 | if protoimpl.UnsafeEnabled { 37 | mi := &file_env_proto_msgTypes[0] 38 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 39 | ms.StoreMessageInfo(mi) 40 | } 41 | } 42 | 43 | func (x *EnvInfo) String() string { 44 | return protoimpl.X.MessageStringOf(x) 45 | } 46 | 47 | func (*EnvInfo) ProtoMessage() {} 48 | 49 | func (x *EnvInfo) ProtoReflect() protoreflect.Message { 50 | mi := &file_env_proto_msgTypes[0] 51 | if protoimpl.UnsafeEnabled && x != nil { 52 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 53 | if ms.LoadMessageInfo() == nil { 54 | ms.StoreMessageInfo(mi) 55 | } 56 | return ms 57 | } 58 | return mi.MessageOf(x) 59 | } 60 | 61 | // Deprecated: Use EnvInfo.ProtoReflect.Descriptor instead. 62 | func (*EnvInfo) Descriptor() ([]byte, []int) { 63 | return file_env_proto_rawDescGZIP(), []int{0} 64 | } 65 | 66 | func (x *EnvInfo) GetName() string { 67 | if x != nil { 68 | return x.Name 69 | } 70 | return "" 71 | } 72 | 73 | func (x *EnvInfo) GetUser() string { 74 | if x != nil { 75 | return x.User 76 | } 77 | return "" 78 | } 79 | 80 | func (x *EnvInfo) GetDetail() string { 81 | if x != nil { 82 | return x.Detail 83 | } 84 | return "" 85 | } 86 | 87 | func (x *EnvInfo) GetMetadata() map[string]string { 88 | if x != nil { 89 | return x.Metadata 90 | } 91 | return nil 92 | } 93 | 94 | var File_env_proto protoreflect.FileDescriptor 95 | 96 | var file_env_proto_rawDesc = []byte{ 97 | 0x0a, 0x09, 0x65, 0x6e, 0x76, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x51, 0x51, 0x54, 98 | 0x56, 0x22, 0xbf, 0x01, 0x0a, 0x07, 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 99 | 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 100 | 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 101 | 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 102 | 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x37, 0x0a, 103 | 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 104 | 0x1b, 0x2e, 0x51, 0x51, 0x54, 0x56, 0x2e, 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4d, 105 | 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 106 | 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 107 | 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 108 | 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 109 | 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 110 | 0x02, 0x38, 0x01, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x51, 0x51, 0x54, 111 | 0x56, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 112 | } 113 | 114 | var ( 115 | file_env_proto_rawDescOnce sync.Once 116 | file_env_proto_rawDescData = file_env_proto_rawDesc 117 | ) 118 | 119 | func file_env_proto_rawDescGZIP() []byte { 120 | file_env_proto_rawDescOnce.Do(func() { 121 | file_env_proto_rawDescData = protoimpl.X.CompressGZIP(file_env_proto_rawDescData) 122 | }) 123 | return file_env_proto_rawDescData 124 | } 125 | 126 | var file_env_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 127 | var file_env_proto_goTypes = []interface{}{ 128 | (*EnvInfo)(nil), // 0: QQTV.EnvInfo 129 | nil, // 1: QQTV.EnvInfo.MetadataEntry 130 | } 131 | var file_env_proto_depIdxs = []int32{ 132 | 1, // 0: QQTV.EnvInfo.metadata:type_name -> QQTV.EnvInfo.MetadataEntry 133 | 1, // [1:1] is the sub-list for method output_type 134 | 1, // [1:1] is the sub-list for method input_type 135 | 1, // [1:1] is the sub-list for extension type_name 136 | 1, // [1:1] is the sub-list for extension extendee 137 | 0, // [0:1] is the sub-list for field type_name 138 | } 139 | 140 | func init() { file_env_proto_init() } 141 | func file_env_proto_init() { 142 | if File_env_proto != nil { 143 | return 144 | } 145 | if !protoimpl.UnsafeEnabled { 146 | file_env_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 147 | switch v := v.(*EnvInfo); i { 148 | case 0: 149 | return &v.state 150 | case 1: 151 | return &v.sizeCache 152 | case 2: 153 | return &v.unknownFields 154 | default: 155 | return nil 156 | } 157 | } 158 | } 159 | type x struct{} 160 | out := protoimpl.TypeBuilder{ 161 | File: protoimpl.DescBuilder{ 162 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 163 | RawDescriptor: file_env_proto_rawDesc, 164 | NumEnums: 0, 165 | NumMessages: 2, 166 | NumExtensions: 0, 167 | NumServices: 0, 168 | }, 169 | GoTypes: file_env_proto_goTypes, 170 | DependencyIndexes: file_env_proto_depIdxs, 171 | MessageInfos: file_env_proto_msgTypes, 172 | }.Build() 173 | File_env_proto = out.File 174 | file_env_proto_rawDesc = nil 175 | file_env_proto_goTypes = nil 176 | file_env_proto_depIdxs = nil 177 | } 178 | -------------------------------------------------------------------------------- /website/Litv/litv.go: -------------------------------------------------------------------------------- 1 | package Litv 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "strconv" 12 | "strings" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | type RpcData struct { 17 | Jsonrpc string `json:"jsonrpc,omitempty"` 18 | Id int `json:"id"` 19 | Method string `json:"method"` 20 | Params interface{} `json:"params"` 21 | Data map[string]interface{} `json:"data,omitempty"` 22 | } 23 | type RpcRet struct { 24 | Jsonrpc string `json:"jsonrpc"` 25 | Result RpcResult `json:"result"` 26 | Error RpcError `json:"error"` 27 | Id int `json:"id"` 28 | } 29 | type RpcError struct { 30 | Code int `json:"code"` 31 | Message string `json:"message"` 32 | Data interface{} `json:"data"` 33 | } 34 | type RpcResult struct { 35 | DataVersion string `json:"data_version"` 36 | Data struct { 37 | Assets []struct { 38 | AssetId string `json:"asset_id"` 39 | Bitrate string `json:"bitrate"` 40 | Quality string `json:"quality"` 41 | Subtitles []interface{} `json:"subtitles"` 42 | } `json:"assets"` 43 | HasSeasons bool `json:"has_seasons"` 44 | Seasons []Season `json:"seasons"` 45 | ProgramPublishPicType string `json:"program_publish_pic_type"` 46 | Season string `json:"season"` 47 | Episode string `json:"episode"` 48 | Title string `json:"title"` 49 | Picture string `json:"picture"` 50 | Score string `json:"score"` 51 | Quality string `json:"quality"` 52 | Description string `json:"description"` 53 | OriginalTitle interface{} `json:"original_title"` 54 | ContentId string `json:"content_id"` 55 | ContentType string `json:"content_type"` 56 | IsSeries bool `json:"is_series"` 57 | SeriesId string `json:"series_id"` 58 | SeasonName string `json:"season_name"` 59 | IsFinale bool `json:"is_finale"` 60 | EpisodeCount string `json:"episode_count"` 61 | ChargeMode string `json:"charge_mode"` 62 | VideoType string `json:"video_type"` 63 | VideoImage string `json:"video_image"` 64 | SeoKeyword string `json:"seo_keyword"` 65 | SeoDescription string `json:"seo_description"` 66 | CronDesc string `json:"cron_desc"` 67 | } `json:"data"` 68 | } 69 | type Season struct { 70 | Season string `json:"season"` 71 | Title string `json:"title"` 72 | SeasonName string `json:"season_name"` 73 | ContentId string `json:"content_id"` 74 | IsFinale bool `json:"is_finale"` 75 | EpisodeCount string `json:"episode_count"` 76 | PosterBanners []string `json:"poster_banners"` 77 | Episodes []struct { 78 | Episode string `json:"episode"` 79 | EpisodeName string `json:"episode_name"` 80 | ContentId string `json:"content_id"` 81 | ChargeMode string `json:"charge_mode"` 82 | PosterBanners []string `json:"poster_banners"` 83 | Copyright []string `json:"copyright"` 84 | VideoType string `json:"video_type"` 85 | VideoImage string `json:"video_image"` 86 | SecondaryMark string `json:"secondary_mark"` 87 | OriginalDate interface{} `json:"original_date"` 88 | GroupId string `json:"group_id"` 89 | } `json:"episodes"` 90 | } 91 | 92 | var ( 93 | rpcApi = "https://proxy.svc.litv.tv/cdi/v2/rpc" 94 | web = "LITV" 95 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 96 | ) 97 | 98 | func NewRpcData(method string, params map[string]string) *RpcData { 99 | p := map[string]string{ 100 | "version": "3.0", 101 | "project_num": "LTAGP02", 102 | "client_id": "", 103 | "device_id": "", 104 | "swver": "", 105 | "conditions": "", 106 | } 107 | for k, v := range params { 108 | p[k] = v 109 | } 110 | data := map[string]interface{}{ 111 | "jsonrpc": "2.0", 112 | "id": 1, 113 | "method": method, 114 | "params": p, 115 | } 116 | return &RpcData{ 117 | Jsonrpc: "2.0", 118 | Id: 1, 119 | Method: method, 120 | Params: p, 121 | Data: data, 122 | } 123 | } 124 | 125 | func (r *RpcData) Do() (data RpcRet, err error) { 126 | req := url.NewRequest() 127 | req.Headers = url.NewHeaders() 128 | req.Headers.Add("Content-Type", "application/json") 129 | req.Json = r.Data 130 | resp, err := client.Do("post", rpcApi, req) 131 | if err != nil { 132 | return 133 | } 134 | err = json.Unmarshal(resp.Content, &data) 135 | if err != nil { 136 | return 137 | } 138 | if data.Error.Code != 0 { 139 | err = fmt.Errorf("code: %d, message: %s", data.Error.Code, data.Error.Message) 140 | return 141 | } 142 | return 143 | } 144 | func GetSeriesTree(seriesId string) (data []Season, err error) { 145 | r := NewRpcData("CCCService.GetSeriesTree", map[string]string{ 146 | "series_id": seriesId, 147 | }) 148 | ret, err := r.Do() 149 | if err != nil { 150 | return 151 | } 152 | data = ret.Result.Data.Seasons 153 | return 154 | } 155 | 156 | func GetProgramInformation(contentId string) (data RpcResult, err error) { 157 | r := NewRpcData("CCCService.GetProgramInformation", map[string]string{ 158 | "content_id": contentId, 159 | }) 160 | d, err := r.Do() 161 | if err != nil { 162 | return 163 | } 164 | if d.Result.Data.IsSeries { 165 | seasons, err := GetSeriesTree(d.Result.Data.SeriesId) 166 | if err != nil { 167 | return data, err 168 | } 169 | d.Result.Data.Seasons = seasons 170 | } 171 | data = d.Result 172 | return 173 | } 174 | 175 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 176 | requestId := ctx.Value("requestId").(string) 177 | log.Debugf("site: %s, start GetMateInfo", web) 178 | r = &server.Data{} 179 | us := strings.Split(sharerUrl, "/") 180 | contentId := us[len(us)-1] 181 | d, err := GetProgramInformation(contentId) 182 | if err != nil { 183 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 184 | code = 1 185 | return 186 | } 187 | data := d.Data 188 | Title := data.Title 189 | r.SeriesTitle = Title 190 | r.SeriesId = data.SeriesId 191 | r.IsVip = data.ChargeMode == "F" 192 | if data.IsSeries { 193 | r.IsSeries = true 194 | seas := data.Seasons 195 | for _, sea := range seas { 196 | SeasonName := sea.SeasonName 197 | episodes := sea.Episodes 198 | for _, episode := range episodes { 199 | SecondaryMark := episode.SecondaryMark 200 | e, _ := strconv.Atoi(episode.Episode) 201 | r.VideoList = append(r.VideoList, &server.Video{ 202 | EpisodeTitle: SeasonName + " " + SecondaryMark, 203 | EpisodeId: episode.ContentId, 204 | IsVip: episode.ChargeMode == "F", 205 | Episode: uint32(e), 206 | }) 207 | } 208 | } 209 | } else { 210 | b, _ := json.Marshal(data.Assets) 211 | r.Extra = map[string]string{ 212 | "assets": string(b), 213 | } 214 | r.IsSeries = false 215 | } 216 | return 217 | 218 | } 219 | -------------------------------------------------------------------------------- /website/MytvSuper/mytvsuper.go: -------------------------------------------------------------------------------- 1 | package MytvSuper 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "regexp" 11 | "sort" 12 | "strconv" 13 | "time" 14 | "trpc.group/trpc-go/trpc-go/log" 15 | ) 16 | 17 | var ( 18 | web = "mytvsuper" 19 | GetEpisodeListUrl = "https://content-api.mytvsuper.com/v1/episode/list?programme_id=%s&start_episode_no=1&end_episode_no=%d&sort_desc=true&platform=web" 20 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 21 | GetDetailsUrl = "https://content-api.mytvsuper.com/v1/programme/details?programme_id=%s&platform=web" 22 | ) 23 | 24 | type Details struct { 25 | Error string `json:"Error"` 26 | ProgrammeId int `json:"programme_id"` 27 | NameTc string `json:"name_tc"` 28 | NameEn string `json:"name_en"` 29 | Path string `json:"path"` 30 | PayStartTime time.Time `json:"pay_start_time"` 31 | PayEndTime time.Time `json:"pay_end_time"` 32 | FreeStartTime time.Time `json:"free_start_time"` 33 | FreeEndTime time.Time `json:"free_end_time"` 34 | StartTime time.Time `json:"start_time"` 35 | EndTime time.Time `json:"end_time"` 36 | Enabled bool `json:"enabled"` 37 | ShortDescTc string `json:"short_desc_tc"` 38 | ShortDescEn string `json:"short_desc_en"` 39 | LongDescTc string `json:"long_desc_tc"` 40 | LongDescEn string `json:"long_desc_en"` 41 | Image struct { 42 | PortraitSmall string `json:"portrait_small"` 43 | PortraitMedium string `json:"portrait_medium"` 44 | PortraitLarge string `json:"portrait_large"` 45 | LandscapeSmall string `json:"landscape_small"` 46 | LandscapeMedium string `json:"landscape_medium"` 47 | LandscapeLarge string `json:"landscape_large"` 48 | } `json:"image"` 49 | VersionType string `json:"version_type"` 50 | ParentalLock bool `json:"parental_lock"` 51 | NumOfEpisodes int `json:"num_of_episodes"` 52 | LatestEpisodeNo int `json:"latest_episode_no"` 53 | Tags []struct { 54 | TagId int `json:"tag_id"` 55 | Type string `json:"type"` 56 | NameTc string `json:"name_tc"` 57 | NameEn string `json:"name_en"` 58 | } `json:"tags"` 59 | Artists []struct { 60 | RefType string `json:"ref_type"` 61 | RefId int `json:"ref_id"` 62 | NameTc string `json:"name_tc"` 63 | NameEn string `json:"name_en"` 64 | } `json:"artists"` 65 | DirectorsTc []interface{} `json:"directors_tc"` 66 | DirectorsEn []interface{} `json:"directors_en"` 67 | EpisodeGroups []struct { 68 | StartEpisodeNo int `json:"start_episode_no"` 69 | EndEpisodeNo int `json:"end_episode_no"` 70 | GroupNameTc string `json:"group_name_tc"` 71 | GroupNameEn string `json:"group_name_en"` 72 | } `json:"episode_groups"` 73 | DisplayEpiTitleOnly bool `json:"display_epi_title_only"` 74 | ShowOffshelf bool `json:"show_offshelf"` 75 | Adunit string `json:"adunit"` 76 | AdtargetGenre []string `json:"adtarget_genre"` 77 | AdtargetCategory []string `json:"adtarget_category"` 78 | AdtargetSubCategory []string `json:"adtarget_sub_category"` 79 | AdCustomParams struct { 80 | Brand string `json:"brand"` 81 | Channel string `json:"channel"` 82 | Contentrelated string `json:"contentrelated"` 83 | Countryoforigin string `json:"countryoforigin"` 84 | Decade string `json:"decade"` 85 | Familycode string `json:"familycode"` 86 | Othercat string `json:"othercat"` 87 | Prodyear string `json:"prodyear"` 88 | Programmetemplate string `json:"programmetemplate"` 89 | Seasonal string `json:"seasonal"` 90 | Shootinglocation string `json:"shootinglocation"` 91 | Topical string `json:"topical"` 92 | Versiontype string `json:"versiontype"` 93 | } `json:"ad_custom_params"` 94 | SeoDescTc string `json:"seo_desc_tc"` 95 | SeoDescEn string `json:"seo_desc_en"` 96 | ShortClips []interface{} `json:"short_clips"` 97 | CallAd bool `json:"call_ad"` 98 | RecommendOtherTitle bool `json:"recommend_other_title"` 99 | GoldContent bool `json:"gold_content"` 100 | ProfileClass []string `json:"profile_class"` 101 | ModifiedAt time.Time `json:"modified_at"` 102 | LabellingGroup []interface{} `json:"labelling_group"` 103 | } 104 | type Episode struct { 105 | Error string `json:"Error"` 106 | Items []struct { 107 | EpisodeId int `json:"episode_id"` 108 | VideoId int `json:"video_id"` 109 | NameTc string `json:"name_tc"` 110 | NameEn string `json:"name_en"` 111 | DescTc string `json:"desc_tc"` 112 | DescEn string `json:"desc_en"` 113 | EpisodeNo int `json:"episode_no"` 114 | AllowDownload interface{} `json:"allow_download"` 115 | PayStartTime time.Time `json:"pay_start_time"` 116 | PayEndTime time.Time `json:"pay_end_time"` 117 | FreeStartTime interface{} `json:"free_start_time"` 118 | FreeEndTime interface{} `json:"free_end_time"` 119 | GoldStartTime interface{} `json:"gold_start_time"` 120 | GoldEndTime interface{} `json:"gold_end_time"` 121 | EarlyRelease []interface{} `json:"early_release"` 122 | IsPreview interface{} `json:"is_preview"` 123 | Image struct { 124 | Small string `json:"small"` 125 | Medium string `json:"medium"` 126 | Large string `json:"large"` 127 | } `json:"image"` 128 | Duration float64 `json:"duration"` 129 | } `json:"items"` 130 | } 131 | 132 | func getProgrammeId(u string) string { 133 | re, _ := regexp.Compile(`_(\d+)/`) 134 | programmeId := re.FindStringSubmatch(u) 135 | if len(programmeId) == 0 { 136 | return "" 137 | } 138 | return programmeId[1] 139 | } 140 | func EpisodeList(programmeId string, end int) (e Episode, err error) { 141 | url := fmt.Sprintf(GetEpisodeListUrl, programmeId, end) 142 | rsp, err := client.Do("get", url, nil) 143 | if err != nil { 144 | return 145 | } 146 | err = json.Unmarshal(rsp.Content, &e) 147 | if e.Error != "" { 148 | err = fmt.Errorf(e.Error) 149 | return 150 | } 151 | return 152 | } 153 | func getDetails(programmeId string) (d Details, err error) { 154 | url := fmt.Sprintf(GetDetailsUrl, programmeId) 155 | rsp, _ := client.Do("get", url, nil) 156 | err = json.Unmarshal(rsp.Content, &d) 157 | if err != nil { 158 | return 159 | } 160 | if d.Error != "" { 161 | err = fmt.Errorf(d.Error) 162 | return 163 | } 164 | return 165 | 166 | } 167 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 168 | requestId := ctx.Value("requestId").(string) 169 | log.Debugf("site: %s, start GetMateInfo", web) 170 | r = &server.Data{} 171 | r = &server.Data{} 172 | v := getProgrammeId(sharerUrl) 173 | if v == "" { 174 | err := fmt.Errorf("invalid URL") 175 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 176 | code = 2 177 | return 178 | 179 | } 180 | d, err := getDetails(v) 181 | if err != nil { 182 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 183 | code = 1 184 | return 185 | } 186 | r.SeriesTitle = d.NameTc 187 | r.SeriesId = strconv.Itoa(d.ProgrammeId) 188 | r.Extra = map[string]string{ 189 | "name_en": d.NameEn, 190 | } 191 | e, err := EpisodeList(v, d.LatestEpisodeNo) 192 | if err != nil { 193 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 194 | code = 1 195 | return 196 | } 197 | for _, v := range e.Items { 198 | r.VideoList = append(r.VideoList, &server.Video{ 199 | EpisodeTitle: v.NameTc, 200 | EpisodeId: strconv.Itoa(v.EpisodeId), 201 | Episode: uint32(v.EpisodeNo), 202 | Extra: map[string]string{ 203 | "name_en": v.NameEn, 204 | "video_id": strconv.Itoa(v.VideoId), 205 | }, 206 | }) 207 | } 208 | sort.Sort(r) 209 | return 210 | } 211 | -------------------------------------------------------------------------------- /website/QQTV/WETV.go: -------------------------------------------------------------------------------- 1 | package QQTV 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | proto2 "github.com/Tontonnow/ddddhmlist/server" 8 | "github.com/wangluozhe/requests/url" 9 | "google.golang.org/protobuf/proto" 10 | "strconv" 11 | "strings" 12 | "trpc.group/trpc-go/trpc-go/log" 13 | ) 14 | 15 | type TxTrpc struct { 16 | QQLiveHead *RequestHead 17 | RequestId int 18 | Caller string 19 | Headers map[string]string 20 | videoList []*VideoItemData 21 | } 22 | 23 | var ( 24 | CountryCode = []int32{153514, 153505, 153564, 153513, 153548, 153560, 153569, 153512} 25 | ) 26 | 27 | func NewTxTrpc() *TxTrpc { 28 | return &TxTrpc{ 29 | RequestId: 1, 30 | Caller: "com.tencent.qqlivei18n", 31 | Headers: map[string]string{ 32 | "User-Agent": "okhttp/3.12.13", 33 | }, 34 | QQLiveHead: &RequestHead{ 35 | RequestId: 0, 36 | VersionInfo: &VersionInfo{ 37 | AppId: "1200009", 38 | }, 39 | LocationInfo: &LocationInfo{ 40 | CountryCode: 153505, 41 | LangCode: 1491963, 42 | }, 43 | }, 44 | } 45 | } 46 | 47 | func (tx *TxTrpc) GenerateRequest(pbRspBody []byte, callee []byte, funcName []byte) ([]byte, error) { 48 | r := Request{ 49 | RequestId: int32(tx.RequestId), 50 | Caller: []byte(tx.Caller), 51 | Callee: callee, 52 | Func: funcName, 53 | } 54 | qqliveHead := tx.QQLiveHead 55 | qqliveHead.RequestId = int32(tx.RequestId) 56 | qqliveHead.Callee = string(callee) 57 | qqliveHead.Func = string(funcName) 58 | tx.RequestId += 1 59 | bytesQQLiveHead, err := proto.Marshal(qqliveHead) 60 | if err != nil { 61 | log.Error("wetv", "GenerateRequest", err) 62 | return nil, err 63 | } 64 | r.TransInfo = map[string][]byte{ 65 | "qqlive_head": bytesQQLiveHead, 66 | } 67 | 68 | pbRspHead, err := proto.Marshal(&r) 69 | if err != nil { 70 | log.Error("wetv", "GenerateRequest", err) 71 | return nil, err 72 | } 73 | 74 | var bytesRspBody bytes.Buffer 75 | bytesRspBody.Write([]byte("\x09\x30\x00\x00")) 76 | rspPkgTotalLen := len(pbRspBody) + len(pbRspHead) + 16 77 | bytesRspBody.Write([]byte{byte(rspPkgTotalLen >> 24), byte(rspPkgTotalLen >> 16), byte(rspPkgTotalLen >> 8), byte(rspPkgTotalLen)}) 78 | pbRspHeadLen := len(pbRspHead) 79 | bytesRspBody.Write([]byte{byte(pbRspHeadLen >> 8), byte(pbRspHeadLen), 0, 0, 0, 0, 0, 0}) 80 | bytesRspBody.Write(pbRspHead) 81 | bytesRspBody.Write(pbRspBody) 82 | 83 | return bytesRspBody.Bytes(), nil 84 | } 85 | func (tx *TxTrpc) parseResponse(data []byte) ([]byte, error) { 86 | if len(data) < 16 { 87 | return nil, nil 88 | } 89 | pbRspHeadLen := int(data[8])<<8 + int(data[9]) 90 | pbRspHead := data[16 : 16+pbRspHeadLen] 91 | Response := &Response{} 92 | err := proto.Unmarshal(pbRspHead, Response) 93 | if err != nil { 94 | return nil, err 95 | } 96 | if Response.ErrorMsg != "" { 97 | return nil, fmt.Errorf(Response.ErrorMsg) 98 | } 99 | pbRspBody := data[16+pbRspHeadLen:] 100 | return pbRspBody, nil 101 | } 102 | func (tx *TxTrpc) GetVideoList(cid string, page int, dataKey string, pageContext string) error { 103 | if dataKey == "" { 104 | dataKey = "cid=" + cid + "&list=1" 105 | 106 | } 107 | if pageContext == "" { 108 | pageContext = "page_index=" + strconv.Itoa(page) + "&page_size=30" 109 | } 110 | request := &DetailMoreListReq{ 111 | DataKey: dataKey, 112 | PageContext: pageContext, 113 | } 114 | data, err := proto.Marshal(request) 115 | if err != nil { 116 | log.Error("wetv", "GenerateRequest", err) 117 | return err 118 | } 119 | payload, err := tx.GenerateRequest(data, []byte("trpc.video_app_international.trpc_detail_list.VideoDetail"), []byte("/trpc.video_app_international.trpc_detail_list.VideoDetail/GetDetailVideoList")) 120 | if err != nil { 121 | log.Error("wetv", "GenerateRequest", err) 122 | return err 123 | } 124 | req := url.NewRequest() 125 | req.Body = string(payload) 126 | r, err := client.Do("post", "https://pbacc.wetvinfo.com/trpc.video_app_international.trpc_detail_list", req) 127 | if err != nil { 128 | log.Error("wetv", "GenerateRequest", err) 129 | return err 130 | } 131 | RspBody, err := tx.parseResponse(r.Content) 132 | if err != nil { 133 | log.Error("wetv", "GenerateRequest", err) 134 | return err 135 | } 136 | DetailVideoListRsp := &DetailVideoListRsp{} 137 | err = proto.Unmarshal(RspBody, DetailVideoListRsp) 138 | if err != nil { 139 | log.Error("wetv", "GenerateRequest", err) 140 | return err 141 | } 142 | nextPageInfo := DetailVideoListRsp.NextPageInfo 143 | hasNextPage := nextPageInfo.HasNextPage 144 | dK := nextPageInfo.DataKey 145 | pC := nextPageInfo.PageContext 146 | videoList := DetailVideoListRsp.VideoList 147 | tx.videoList = append(tx.videoList, videoList...) 148 | if hasNextPage != nil && *hasNextPage { 149 | return tx.GetVideoList(cid, page+1, *dK, *pC) 150 | } 151 | return nil 152 | } 153 | func (tx *TxTrpc) GetDetailPage(vid string, cid string) (d *DetailPageRsp, err error) { 154 | d = &DetailPageRsp{} 155 | request := &DetailPageReq{ 156 | Vid: vid, 157 | Cid: cid, 158 | } 159 | data, err := proto.Marshal(request) 160 | if err != nil { 161 | log.Error("wetv", "GenerateRequest", err) 162 | return 163 | } 164 | payload, err := tx.GenerateRequest(data, []byte("trpc.video_app_international.trpc_video_detail.VideoDetail"), []byte("/trpc.video_app_international.trpc_video_detail.VideoDetail/GetDetailPage")) 165 | if err != nil { 166 | log.Error("wetv", "GenerateRequest", err) 167 | return 168 | } 169 | req := url.NewRequest() 170 | req.Body = string(payload) 171 | r, err := client.Do("post", "https://pbacc.wetvinfo.com/trpc.video_app_international.trpc_video_detail", req) 172 | if err != nil { 173 | log.Error("wetv", "GenerateRequest", err) 174 | return 175 | } 176 | RspBody, err := tx.parseResponse(r.Content) 177 | if err != nil { 178 | log.Error("wetv", "GenerateRequest", err) 179 | return 180 | } 181 | err = proto.Unmarshal(RspBody, d) 182 | if err != nil { 183 | log.Error("wetv", "GenerateRequest", err) 184 | return 185 | } 186 | return 187 | } 188 | func AddVideoList(ret *proto2.Data, videoList []*VideoItemData) { 189 | for _, v := range videoList { 190 | if v.Vid == nil || v.Title == nil { 191 | continue 192 | } 193 | IsDrm := "false" 194 | if v.IsDrm != nil { 195 | IsDrm = strconv.FormatBool(*v.IsDrm) 196 | } 197 | if v.EpisodeId == nil { 198 | v.EpisodeId = proto.Int32(-1) 199 | } 200 | ret.VideoList = append(ret.VideoList, &proto2.Video{ 201 | EpisodeId: *v.Vid, 202 | EpisodeTitle: *v.Title, 203 | Episode: uint32(*v.EpisodeId), 204 | Extra: map[string]string{ 205 | "isDrm": IsDrm, 206 | }, 207 | }) 208 | } 209 | } 210 | func GetWeTvMateInfo(ctx context.Context, sharerUrl string) (ret *proto2.Data, code int) { 211 | var err error 212 | requestId := ctx.Value("requestId").(string) 213 | log.Debugf("site: %s, start GetMateInfo", web) 214 | ret = &proto2.Data{} 215 | cid := ExtractCid(sharerUrl) 216 | if cid == "" { 217 | err = fmt.Errorf("url is invalid") 218 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 219 | code = 2 220 | return 221 | } 222 | if strings.Contains(cid, "-") { 223 | cid = strings.Split(cid, "-")[0] 224 | } 225 | tx := NewTxTrpc() 226 | var d *DetailPageRsp 227 | for _, Ccode := range CountryCode { 228 | tx.QQLiveHead.LocationInfo.CountryCode = Ccode 229 | d, err = tx.GetDetailPage("", cid) 230 | if err != nil { 231 | if strings.Contains(err.Error(), "版权限制") { 232 | continue 233 | } 234 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 235 | code = 1 236 | return 237 | } 238 | break 239 | } 240 | 241 | for _, feed := range d.FeedList { 242 | switch *feed.Type { 243 | case "FeedDetailsInfo": 244 | var feedDetailsInfo FeedDetailsInfo 245 | err = proto.Unmarshal(feed.FeedData, &feedDetailsInfo) 246 | ret.SeriesTitle = *feedDetailsInfo.Title 247 | break 248 | case "FeedDetailsToolbar": 249 | var feedDetailsToolbar FeedDetailsToolbar 250 | err = proto.Unmarshal(feed.FeedData, &feedDetailsToolbar) 251 | break 252 | case "FeedPlayListHorizontal": 253 | var feedDetailsRecommend FeedPlayListHorizontal 254 | err = proto.Unmarshal(feed.FeedData, &feedDetailsRecommend) 255 | if err != nil { 256 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 257 | code = 1 258 | return 259 | } 260 | if feedDetailsRecommend.VideoList != nil && len(feedDetailsRecommend.VideoList) > 0 { 261 | ret.SeriesId = *feedDetailsRecommend.VideoList[0].Cid 262 | AddVideoList(ret, feedDetailsRecommend.VideoList) 263 | } 264 | break 265 | } 266 | } 267 | if len(ret.VideoList) == 0 { 268 | err = tx.GetVideoList(cid, 0, "", "") 269 | if err != nil { 270 | return nil, 2 271 | } 272 | AddVideoList(ret, tx.videoList) 273 | } 274 | return ret, 0 275 | } 276 | -------------------------------------------------------------------------------- /website/Friday/Friday.go: -------------------------------------------------------------------------------- 1 | package Friday 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "regexp" 12 | "strconv" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | var ( 17 | web = "friday" 18 | headers = map[string]string{ 19 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36", 20 | "Platform": "Android Phone", 21 | "Version": "905", 22 | "Model": "Pixel", 23 | "UDID": config.Conf.AndroidId, 24 | "Device-Token": "", 25 | } 26 | ContentType = map[string]int{ 27 | "movie": 1, 28 | "drama": 2, 29 | "anime": 3, 30 | "tvshow": 4, 31 | "show": 4, 32 | } 33 | re = regexp.MustCompile(`video.friday.tw/(drama|anime|movie|show|tvshow)/detail/(\d+)`) 34 | getEpisode = "http://vbmspxy.video.friday.tw/apiv2/content/get?contentId=%d&contentType=%d&eventPageId=01&length=%d&offset=%d&recommendId=-1" 35 | getToken = "http://vbmspxy.video.friday.tw/apiv2/token/getv2" 36 | Headers = url.ParseHeaders(headers) 37 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 38 | ) 39 | 40 | type Episode struct { 41 | ContentId int `json:"contentId"` 42 | ContentType int `json:"contentType"` 43 | StreamingId int `json:"streamingId"` 44 | StreamingType int `json:"streamingType"` 45 | EpisodeName string `json:"episodeName"` 46 | Sort string `json:"sort"` 47 | ChineseName string `json:"chineseName"` 48 | EnglishName string `json:"englishName"` 49 | Duration string `json:"duration"` 50 | StillImageUrl string `json:"stillImageUrl"` 51 | SeparationName string `json:"separationName"` 52 | SeparationIntroduction string `json:"separationIntroduction"` 53 | IsPlay int `json:"isPlay"` 54 | PaymentTagList []int `json:"paymentTagList"` 55 | EnableAd bool `json:"enableAd"` 56 | } 57 | 58 | type Response struct { 59 | Status int `json:"status"` 60 | Code string `json:"code"` 61 | Message string `json:"message"` 62 | Data struct { 63 | AccessToken string `json:"accessToken"` 64 | Scope string `json:"scope"` 65 | ExpiresIn int `json:"expiresIn"` 66 | IdToken string `json:"idToken"` 67 | LoginType int `json:"LoginType"` 68 | Content struct { 69 | EpisodeList []Episode `json:"episodeList"` 70 | ContentId int `json:"contentId"` 71 | ContentType int `json:"contentType"` 72 | Rating int `json:"rating"` 73 | ChineseName string `json:"chineseName"` 74 | EnglishName string `json:"englishName"` 75 | Introduction string `json:"introduction"` 76 | Year string `json:"year"` 77 | Duration string `json:"duration"` 78 | FridayScore float64 `json:"fridayScore"` 79 | ScoreCount int `json:"scoreCount"` 80 | ImageUrl string `json:"imageUrl"` 81 | StillImageUrl string `json:"stillImageUrl"` 82 | PropertyTagList []int `json:"propertyTagList"` 83 | BroadcastTime string `json:"broadcastTime"` 84 | Want int `json:"want"` 85 | Area string `json:"area"` 86 | StreamingId int `json:"streamingId"` 87 | StreamingType int `json:"streamingType"` 88 | CanPlay bool `json:"canPlay"` 89 | PaymentTagList []int `json:"paymentTagList"` 90 | IsEmpty bool `json:"isEmpty"` 91 | MirrorEnable bool `json:"mirrorEnable"` 92 | EffectiveDateTime string `json:"effectiveDateTime"` 93 | ExpireDate string `json:"expireDate"` 94 | EnableAirPlay bool `json:"enableAirPlay"` 95 | EnableChromecast bool `json:"enableChromecast"` 96 | TwOnlineTime string `json:"twOnlineTime"` 97 | TwBoxOffice string `json:"twBoxOffice"` 98 | GlobalBoxOffice string `json:"globalBoxOffice"` 99 | CategoryList []struct { 100 | CategoryId int `json:"categoryId"` 101 | Name string `json:"name"` 102 | } `json:"categoryList"` 103 | ContentTagList []struct { 104 | ContentTag int `json:"contentTag"` 105 | Name string `json:"name"` 106 | } `json:"contentTagList"` 107 | ArtistList []struct { 108 | ChineseName string `json:"chineseName"` 109 | EnglishName string `json:"englishName,omitempty"` 110 | ArtistType int `json:"artistType"` 111 | } `json:"artistList"` 112 | ShortIntroduction string `json:"shortIntroduction"` 113 | AwardsList []int `json:"awardsList"` 114 | FinalPlayList []int `json:"finalPlayList"` 115 | CanBook bool `json:"canBook"` 116 | IsBook bool `json:"isBook"` 117 | SrcRecommendId string `json:"srcRecommendId"` 118 | RecommendId string `json:"recommendId"` 119 | SrcEventPageId string `json:"srcEventPageId"` 120 | IsMultiView bool `json:"isMultiView"` 121 | StreamingListScene []int `json:"streamingListScene"` 122 | StreamingListPerson []int `json:"streamingListPerson"` 123 | EnableAutoPlay bool `json:"enableAutoPlay"` 124 | EnableAd bool `json:"enableAd"` 125 | } 126 | } `json:"data"` 127 | } 128 | 129 | func SendRequest(method, u string) (rsp Response, err error) { 130 | Req := url.NewRequest() 131 | Req.Headers = Headers 132 | r, err := client.Do(method, u, Req) 133 | if err != nil { 134 | return 135 | } 136 | err = json.Unmarshal(r.Content, &rsp) 137 | if err != nil { 138 | return 139 | } 140 | if rsp.Status != 200 { 141 | err = fmt.Errorf(rsp.Message) 142 | return 143 | } 144 | return 145 | } 146 | func RefreshToken() (err error) { 147 | rsp, err := SendRequest("POST", getToken) 148 | if err != nil { 149 | return 150 | } 151 | Headers.Add("Authorization", "Bearer "+rsp.Data.AccessToken) 152 | log.Debug("Friday RefreshToken Success: ", rsp.Data.AccessToken) 153 | return 154 | } 155 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 156 | var ( 157 | err error 158 | contentId, contentType, offset, length int 159 | ) 160 | r = &server.Data{} 161 | requestId := ctx.Value("requestId").(string) 162 | log.Debugf("site: %s, start GetMateInfo", web) 163 | offset = 0 164 | length = 500 165 | m := re.FindStringSubmatch(sharerUrl) 166 | if len(m) != 3 { 167 | err = fmt.Errorf("URL Error: %s", sharerUrl) 168 | log.Errorf("site: %s, requestId: %s, error: %v", web, requestId, err) 169 | code = 2 170 | return 171 | 172 | } 173 | contentId, _ = strconv.Atoi(m[2]) 174 | contentType = ContentType[m[1]] 175 | var EpisodeList []Episode 176 | for { 177 | sharerUrl = fmt.Sprintf(getEpisode, contentId, contentType, length, offset) 178 | d, err := SendRequest("POST", sharerUrl) 179 | if err != nil { 180 | if err.Error() == "權限不足" { 181 | err = RefreshToken() 182 | if err != nil { 183 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 184 | code = 1 185 | return 186 | } 187 | continue 188 | } 189 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 190 | code = 1 191 | return 192 | } 193 | content := d.Data.Content 194 | r.SeriesTitle = content.ChineseName 195 | r.SeriesId = strconv.Itoa(content.ContentId) 196 | if content.EpisodeList != nil { 197 | EpisodeList = append(EpisodeList, content.EpisodeList...) 198 | if len(content.EpisodeList) == length { 199 | offset += length 200 | continue 201 | } 202 | r.IsSeries = true 203 | for _, v := range EpisodeList { 204 | episode, _ := strconv.ParseUint(v.Sort, 10, 32) 205 | r.VideoList = append(r.VideoList, &server.Video{ 206 | EpisodeId: strconv.Itoa(v.ContentId), 207 | Episode: uint32(episode), 208 | EpisodeTitle: v.ChineseName + " " + v.SeparationName, 209 | Extra: map[string]string{ 210 | "ContentId": strconv.Itoa(v.ContentId), 211 | "ContentType": strconv.Itoa(v.ContentType), 212 | "StreamingId": strconv.Itoa(v.StreamingId), 213 | "StreamingType": strconv.Itoa(v.StreamingType), 214 | "EnglishName": v.EnglishName, 215 | }, 216 | }) 217 | 218 | } 219 | } else { 220 | r.IsSeries = false 221 | r.Extra = map[string]string{ 222 | "ContentId": strconv.Itoa(content.ContentId), 223 | "ContentType": strconv.Itoa(content.ContentType), 224 | "StreamingId": strconv.Itoa(content.StreamingId), 225 | "StreamingType": strconv.Itoa(content.StreamingType), 226 | "EnglishName": content.EnglishName, 227 | } 228 | } 229 | break 230 | } 231 | return 232 | } 233 | -------------------------------------------------------------------------------- /website/IQ/iq.go: -------------------------------------------------------------------------------- 1 | package IQ 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | j "github.com/Tontonnow/ddddhmlist/utils/jwt" 10 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 11 | "strconv" 12 | "time" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | var ( 17 | web = "iq" 18 | DeviceId = "1008611" 19 | registerMode = "https://tv-api2.iq.com/api/registerMode?sdkintVer=28&apkVer=8.2.0&model=TV&deviceId=%s&uuid=20201127144624572xXkbWBuKY100031&macAddr=0&localLang=zh_cn" 20 | GetEpisode = "https://tv-api2.iq.com/api/v2/episodeListWithPreview/%s?langCode=%s&num=60&gps=1&ua=TV&deviceId=&platform=&isVip=true&sid=&network=1&uid=&pos=%d&modeCode=intl&pspStatus=-1" 21 | header = map[string]string{ 22 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36", 23 | } 24 | GetIqEpgInfo = "https://pcw-api.iq.com/api/epgInfo/%s?platformId=4&langCode=zh_cn&modeCode=intl&deviceId=ew&uid=&pspStatus=" 25 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 26 | ) 27 | 28 | type EpgInfoIQ struct { 29 | Total int `json:"total"` 30 | Pos int `json:"pos"` 31 | HasMore bool `json:"hasMore"` 32 | ChnId int `json:"chnId"` 33 | EpgS []Epg `json:"epg"` 34 | Code string `json:"code"` 35 | SourceCode int `json:"sourceCode"` 36 | AlbumId int `json:"albumId"` 37 | AlbumName string `json:"albumName"` 38 | } 39 | type Epg struct { 40 | QipuId int64 `json:"qipuId"` 41 | QipuIdStr string `json:"qipuIdStr"` 42 | DefMultiImage interface{} `json:"defMultiImage"` 43 | BackGroundPicColor interface{} `json:"backGroundPicColor"` 44 | ChnId int `json:"chnId"` 45 | Name string `json:"name"` 46 | ShortName string `json:"shortName"` 47 | AlbumPic string `json:"albumPic"` 48 | PosterPic interface{} `json:"posterPic"` 49 | AlbumWebpPic string `json:"albumWebpPic"` 50 | Focus string `json:"focus"` 51 | Score string `json:"score"` 52 | Rating string `json:"rating"` 53 | VipInfo struct { 54 | } `json:"vipInfo"` 55 | VipType string `json:"vipType"` 56 | IsExclusive int `json:"isExclusive"` 57 | Is3D int `json:"is3D"` 58 | Is1080 int `json:"is1080"` 59 | IsDolby int `json:"isDolby"` 60 | PublishTime string `json:"publishTime"` 61 | InitIssueTime string `json:"initIssueTime"` 62 | Desc string `json:"desc"` 63 | Drm string `json:"drm"` 64 | Hdr string `json:"hdr"` 65 | SubTitle string `json:"subTitle"` 66 | ContentType int `json:"contentType"` 67 | IsSeries int `json:"isSeries"` 68 | Order int `json:"order"` 69 | AllowRegion interface{} `json:"allowRegion"` 70 | Categories []int64 `json:"categories"` 71 | ShareAllowed interface{} `json:"shareAllowed"` 72 | PlayLocSuffix string `json:"playLocSuffix"` 73 | AlbumLocSuffix interface{} `json:"albumLocSuffix"` 74 | PlayHrefLangPile interface{} `json:"playHrefLangPile"` 75 | AlbumHrefLangPile interface{} `json:"albumHrefLangPile"` 76 | IsQiyiProduced int `json:"isQiyiProduced"` 77 | MatchedLang int `json:"matchedLang"` 78 | AlternativeTitles []interface{} `json:"alternativeTitles"` 79 | AlbumPicColor interface{} `json:"albumPicColor"` 80 | IsFollowFeatured bool `json:"isFollowFeatured"` 81 | InfoControlStatus interface{} `json:"infoControlStatus"` 82 | VolunteerTranslates []interface{} `json:"volunteerTranslates"` 83 | FatherCollectionIds []interface{} `json:"fatherCollectionIds"` 84 | Season int `json:"season"` 85 | FocusImage struct { 86 | FocusImagesWithLang []interface{} `json:"focusImagesWithLang"` 87 | } `json:"focusImage"` 88 | FirstPlayTimeLine interface{} `json:"firstPlayTimeLine"` 89 | FirstPlayTimeOnlyDate interface{} `json:"firstPlayTimeOnlyDate"` 90 | PeopleInfosMap struct { 91 | } `json:"peopleInfosMap"` 92 | AlbumId int `json:"albumId"` 93 | AlbumName string `json:"albumName"` 94 | Len int `json:"len"` 95 | Type4K string `json:"type4k"` 96 | Dolby string `json:"dolby"` 97 | SourceCode int `json:"sourceCode"` 98 | Pic string `json:"pic"` 99 | PrePic interface{} `json:"prePic"` 100 | DefaultVid string `json:"defaultVid"` 101 | Screenshot struct { 102 | ImgUrl string `json:"imgUrl"` 103 | WebImgUrl string `json:"webImgUrl"` 104 | ImageSize string `json:"imageSize"` 105 | Interval int `json:"interval"` 106 | MergeCount string `json:"mergeCount"` 107 | } `json:"screenshot"` 108 | FatherEpisodeId int `json:"fatherEpisodeId"` 109 | MultiEpisodeInfo struct { 110 | MultiEpisodeCount int `json:"multiEpisodeCount"` 111 | MultiEpisodeOrder int `json:"multiEpisodeOrder"` 112 | MultiEpisode bool `json:"multiEpisode"` 113 | } `json:"multiEpisodeInfo"` 114 | ExtraName string `json:"extraName"` 115 | AlbumIdStr string `json:"albumIdStr"` 116 | FatherEpisodeIdStr string `json:"fatherEpisodeIdStr"` 117 | FatherEpisodeIdOrder interface{} `json:"fatherEpisodeIdOrder"` 118 | AlbumDesc interface{} `json:"albumDesc"` 119 | PCount int `json:"pCount"` 120 | PImgSize interface{} `json:"pImgSize"` 121 | CImgSize string `json:"cImgSize"` 122 | CWebpImgSize string `json:"cWebpImgSize"` 123 | } 124 | 125 | func (i *Iqy) RefreshToken() (err error) { 126 | sprintf := fmt.Sprintf(registerMode, DeviceId) 127 | data, err := i.doRequest(sprintf, "token") 128 | if err != nil { 129 | return 130 | } 131 | token, err := data.String() 132 | if err != nil { 133 | return 134 | } 135 | jjj, _ := j.ParseJwtWithClaims(token) 136 | exp, err := jjj.GetExpirationTime() 137 | if err != nil { 138 | return 139 | } 140 | if exp.Sub(time.Now()) < time.Hour { 141 | err = fmt.Errorf("IQ RefreshToken Error: %v", "Token Expired") 142 | return 143 | } 144 | header["Authorization"] = "Bearer " + token 145 | for k, v := range header { 146 | client.Session.Headers.Set(k, v) 147 | } 148 | return 149 | } 150 | func (i *Iqy) GetEpisodeList(aid string) (r []*server.Video, err error) { 151 | u := fmt.Sprintf(GetEpisode, aid, "zh_cn", 0) 152 | data, err := i.doRequest(u, "") 153 | if err != nil { 154 | return 155 | } 156 | b, err := data.MarshalJSON() 157 | if err != nil { 158 | return 159 | 160 | } 161 | vList := EpgInfoIQ{} 162 | err = json.Unmarshal(b, &vList) 163 | if err != nil { 164 | return 165 | } 166 | var v []Epg 167 | v = append(v, vList.EpgS...) 168 | if vList.HasMore { 169 | //每页最多60 170 | var ch = make(chan []Epg, vList.Total/60+1) 171 | for n := 60; n < vList.Total; n += 60 { 172 | go func(n int) { 173 | u := fmt.Sprintf(GetEpisode, aid, "zh_cn", n) 174 | data, err := i.doRequest(u) 175 | if err != nil { 176 | ch <- nil 177 | } 178 | b, err := data.MarshalJSON() 179 | if err != nil { 180 | ch <- nil 181 | } 182 | list := EpgInfoIQ{} 183 | err = json.Unmarshal(b, &list) 184 | if err != nil { 185 | ch <- nil 186 | } 187 | ch <- list.EpgS 188 | }(n) 189 | } 190 | for n := 60; n < vList.Total; n += 60 { 191 | v = append(v, <-ch...) 192 | } 193 | } 194 | for _, e := range v { 195 | r = append(r, &server.Video{ 196 | EpisodeId: strconv.FormatInt(e.QipuId, 10), 197 | EpisodeTitle: e.Name, 198 | IsVip: e.VipInfo != struct{}{}, 199 | Episode: uint32(e.Order), 200 | Extra: map[string]string{ 201 | "subtitle": e.SubTitle, 202 | }, 203 | }) 204 | 205 | } 206 | return 207 | } 208 | func NewIq() *Iqy { 209 | i := &Iqy{} 210 | err := i.RefreshToken() 211 | if err != nil { 212 | return nil 213 | } 214 | return i 215 | } 216 | func (i *Iqy) GetIqMateInfo(sharerUrl string) (r *server.Data, err error) { 217 | r = &server.Data{} 218 | epgInfo, err := i.GetBaseInfo(r, sharerUrl, true) 219 | if err != nil { 220 | i.code = 1 221 | return 222 | } 223 | if epgInfo.ChnId == 1 { 224 | r.SeriesTitle = epgInfo.Name 225 | r.SeriesId = strconv.FormatInt(epgInfo.QipuId, 10) 226 | return 227 | } else { 228 | r.SeriesTitle = epgInfo.AlbumName 229 | r.SeriesId = strconv.Itoa(epgInfo.AlbumId) 230 | r.VideoList, err = i.GetEpisodeList(r.SeriesId) 231 | if err != nil { 232 | i.code = 1 233 | return 234 | } 235 | 236 | } 237 | return 238 | } 239 | func GetIqMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 240 | var ( 241 | err error 242 | ) 243 | r = &server.Data{} 244 | requestId := ctx.Value("requestId").(string) 245 | log.Debugf("site: %s, start GetMateInfo", web) 246 | i := NewIq() 247 | r, err = i.GetIqMateInfo(sharerUrl) 248 | if err != nil { 249 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 250 | } 251 | code = i.code 252 | return 253 | } 254 | -------------------------------------------------------------------------------- /website/KKTV/kktv.go: -------------------------------------------------------------------------------- 1 | package KKTV 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "strings" 11 | "trpc.group/trpc-go/trpc-go/log" 12 | ) 13 | 14 | var ( 15 | web = "kktv" 16 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 17 | ) 18 | 19 | type Mate struct { 20 | Status struct { 21 | Type string `json:"type"` 22 | Subtype string `json:"subtype"` 23 | Message string `json:"message"` 24 | } `json:"status"` 25 | Data struct { 26 | Id string `json:"id"` 27 | Title string `json:"title"` 28 | TitleType string `json:"title_type"` 29 | Copyright string `json:"copyright"` 30 | Cover string `json:"cover"` 31 | Stills []string `json:"stills"` 32 | ContentLabels []interface{} `json:"content_labels"` 33 | ContentLabelsForExpiredUser []interface{} `json:"content_labels_for_expired_user"` 34 | ContentLabelsForFreetrialUser []interface{} `json:"content_labels_for_freetrial_user"` 35 | TitleAliases interface{} `json:"title_aliases"` 36 | Available bool `json:"available"` 37 | IsEnding bool `json:"is_ending"` 38 | IsContainingAvod bool `json:"is_containing_avod"` 39 | ReverseDisplayOrder bool `json:"reverse_display_order"` 40 | IsValidated bool `json:"is_validated"` 41 | FreeTrial bool `json:"free_trial"` 42 | ChildLock bool `json:"child_lock"` 43 | EndYear int `json:"end_year"` 44 | ReleaseYear int `json:"release_year"` 45 | TitleExtra struct { 46 | Id string `json:"id"` 47 | } `json:"title_extra"` 48 | Review struct { 49 | Content string `json:"content"` 50 | } `json:"review"` 51 | ReleaseInfo string `json:"release_info"` 52 | Status string `json:"status"` 53 | Summary string `json:"summary"` 54 | LatestUpdateInfo string `json:"latest_update_info"` 55 | TotalEpisodeCounts struct { 56 | Field1 int `json:"0600167301"` 57 | } `json:"total_episode_counts"` 58 | TotalSeriesCount int `json:"total_series_count"` 59 | Rating int `json:"rating"` 60 | UserRatingCount int `json:"user_rating_count"` 61 | UserRating float64 `json:"user_rating"` 62 | WikiOrig string `json:"wiki_orig"` 63 | WikiZh string `json:"wiki_zh"` 64 | Ost struct { 65 | ArtistName string `json:"artist_name"` 66 | Image string `json:"image"` 67 | Title string `json:"title"` 68 | Url string `json:"url"` 69 | } `json:"ost"` 70 | Country struct { 71 | Id string `json:"id"` 72 | Title string `json:"title"` 73 | CollectionType string `json:"collection_type"` 74 | CollectionName string `json:"collection_name"` 75 | } `json:"country"` 76 | ContentAgents []struct { 77 | Id string `json:"id"` 78 | CollectionType string `json:"collection_type"` 79 | CollectionName string `json:"collection_name"` 80 | } `json:"content_agents"` 81 | ContentProviders interface{} `json:"content_providers"` 82 | Themes []struct { 83 | Id string `json:"id"` 84 | CollectionType string `json:"collection_type"` 85 | CollectionName string `json:"collection_name"` 86 | } `json:"themes"` 87 | Genres []struct { 88 | Id string `json:"id"` 89 | Title string `json:"title"` 90 | CollectionType string `json:"collection_type"` 91 | CollectionName string `json:"collection_name"` 92 | } `json:"genres"` 93 | Tags []struct { 94 | Id string `json:"id"` 95 | CollectionType string `json:"collection_type"` 96 | CollectionName string `json:"collection_name"` 97 | } `json:"tags"` 98 | Directors []struct { 99 | Id string `json:"id"` 100 | CollectionType string `json:"collection_type"` 101 | CollectionName string `json:"collection_name"` 102 | } `json:"directors"` 103 | Writers []struct { 104 | Id string `json:"id"` 105 | CollectionType string `json:"collection_type"` 106 | CollectionName string `json:"collection_name"` 107 | } `json:"writers"` 108 | Producers []struct { 109 | Id string `json:"id"` 110 | CollectionType string `json:"collection_type"` 111 | CollectionName string `json:"collection_name"` 112 | } `json:"producers"` 113 | Casts []struct { 114 | Id string `json:"id"` 115 | CollectionType string `json:"collection_type"` 116 | CollectionName string `json:"collection_name"` 117 | } `json:"casts"` 118 | Series []struct { 119 | AvodEpisodeHint string `json:"avod_episode_hint"` 120 | Id string `json:"id"` 121 | Title string `json:"title"` 122 | Episodes []struct { 123 | Id string `json:"id"` 124 | Title string `json:"title"` 125 | SeriesTitle string `json:"series_title"` 126 | SeriesId string `json:"series_id"` 127 | ContentAgent string `json:"content_agent"` 128 | ContentProvider string `json:"content_provider"` 129 | Duration float64 `json:"duration"` 130 | EndOffset int `json:"end_offset"` 131 | EndYear int `json:"end_year"` 132 | ReleaseYear int `json:"release_year"` 133 | StartYear int `json:"start_year"` 134 | Available bool `json:"available"` 135 | FreeTrial bool `json:"free_trial"` 136 | HasSubtitles bool `json:"has_subtitles"` 137 | IsEnding bool `json:"is_ending"` 138 | IsValidated bool `json:"is_validated"` 139 | OfflineMode bool `json:"offline_mode"` 140 | PlayZone bool `json:"play_zone"` 141 | IsAvod bool `json:"is_avod"` 142 | RoamingMode bool `json:"roaming_mode"` 143 | SecurePlayback bool `json:"secure_playback"` 144 | LicenseEnd int `json:"license_end"` 145 | LicenseStart int `json:"license_start"` 146 | PublishTime int `json:"publish_time"` 147 | Pub int `json:"pub"` 148 | Unpub int `json:"unpub"` 149 | Still string `json:"still"` 150 | Subtitles interface{} `json:"subtitles"` 151 | Mezzanines struct { 152 | Dash struct { 153 | Size int64 `json:"size"` 154 | Sizes map[string]int `json:"sizes"` 155 | Uri string `json:"uri"` 156 | } `json:"dash"` 157 | Hls struct { 158 | Size int64 `json:"size"` 159 | Sizes map[string]int `json:"sizes"` 160 | Uri string `json:"uri"` 161 | } `json:"hls"` 162 | } `json:"mezzanines"` 163 | } `json:"episodes"` 164 | } `json:"series"` 165 | UserActions struct { 166 | } `json:"user_actions"` 167 | } `json:"data"` 168 | } 169 | 170 | func ExtractIDFromURL(u string) (id string) { 171 | if u[:4] != "http" { 172 | return u 173 | } 174 | /* 175 | re := regexp.MustCompile(`https://kktv.me/titles/(\d+)`) 176 | matches := re.FindStringSubmatch(u) 177 | if len(matches) < 2 { 178 | return "" 179 | } 180 | return matches[1] 181 | */ 182 | us := strings.Split(u, "/") 183 | return us[len(us)-1][:8] 184 | 185 | } 186 | 187 | func GetMateInfo(ctx context.Context, sharerUrl string) (ret *server.Data, code int) { 188 | var ( 189 | err error 190 | ) 191 | requestId := ctx.Value("requestId").(string) 192 | log.Debugf("site: %s, start GetMateInfo", web) 193 | ret = &server.Data{} 194 | id := ExtractIDFromURL(sharerUrl) 195 | if id == "" { 196 | err = fmt.Errorf("Invalid URL") 197 | code = 2 198 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 199 | return 200 | } 201 | url := fmt.Sprintf("https://api.kktv.me/v3/titles/%s", id) 202 | r, err := client.Do("get", url, nil) 203 | if err != nil { 204 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 205 | code = 1 206 | return 207 | } 208 | var t Mate 209 | err = json.Unmarshal(r.Content, &t) 210 | if err != nil { 211 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 212 | code = 1 213 | return 214 | } 215 | if t.Status.Type != "OK" { 216 | err = fmt.Errorf(t.Status.Message) 217 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 218 | code = 1 219 | return 220 | } 221 | ret.SeriesTitle = t.Data.Title 222 | ret.SeriesId = t.Data.Id 223 | if t.Data.ContentLabelsForExpiredUser == nil { 224 | ret.IsVip = false 225 | } 226 | for _, v := range t.Data.Series { 227 | for _, e := range v.Episodes { 228 | ret.VideoList = append(ret.VideoList, &server.Video{ 229 | EpisodeTitle: v.Title + " " + e.Title, 230 | EpisodeId: e.Id, 231 | Extra: map[string]string{ 232 | "dash": e.Mezzanines.Dash.Uri, 233 | "hls": e.Mezzanines.Hls.Uri, 234 | }, 235 | IsVip: !e.PlayZone, 236 | Episode: uint32(int(e.Id[len(e.Id)-1]) - 48), 237 | }) 238 | } 239 | } 240 | return 241 | 242 | } 243 | -------------------------------------------------------------------------------- /website/MyVideo/myvideo.go: -------------------------------------------------------------------------------- 1 | package MyVideo 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/Tontonnow/ddddhmlist/config" 9 | "github.com/Tontonnow/ddddhmlist/server" 10 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 11 | "strings" 12 | "trpc.group/trpc-go/trpc-go/log" 13 | ) 14 | 15 | var ( 16 | web = "myvideo" 17 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 18 | FindMyVideoContent6 = "https://mgw.myvideo.net.tw/twmsgw.api/FindMyVideoContent6.json?chl=android&chk=9e76ab&contentId=%s&isSeries=%s&devType=Handset" 19 | GetVideoListDataUrl = "https://myvideo.net.tw/ajax/ajaxGetVideoListData?twmContentId=%s" 20 | ) 21 | 22 | type MyVideo struct { 23 | Name string `json:"name"` 24 | Status struct { 25 | Code string `json:"code"` 26 | Message string `json:"message"` 27 | } `json:"status"` 28 | Data Content6 `json:"data"` 29 | } 30 | type Content6 struct { 31 | Video2 struct { 32 | Id string `json:"id"` 33 | VideoTitle string `json:"videoTitle"` 34 | Episode string `json:"episode"` 35 | VolumnDesc string `json:"volumnDesc"` 36 | VideoPic01 string `json:"videoPic01"` 37 | VideoPic02 string `json:"videoPic02"` 38 | VideoPic03 string `json:"videoPic03"` 39 | VideoPic04 string `json:"videoPic04"` 40 | VideoPic05 string `json:"videoPic05"` 41 | VideoPic06 string `json:"videoPic06"` 42 | VideoPic07 string `json:"videoPic07"` 43 | SeriesType string `json:"seriesType"` 44 | SubTitle string `json:"subTitle"` 45 | PublishYear string `json:"publishYear"` 46 | MovieLength string `json:"movieLength"` 47 | MovieLengthDesc string `json:"movieLengthDesc"` 48 | IsHD string `json:"isHD"` 49 | VideoQuality string `json:"videoQuality"` 50 | InStoreService string `json:"inStoreService"` 51 | MainCategory string `json:"mainCategory"` 52 | VideoDesc string `json:"videoDesc"` 53 | Language string `json:"language"` 54 | PublishDate string `json:"publishDate"` 55 | HasTrailer string `json:"hasTrailer"` 56 | TrailerUrl string `json:"trailerUrl"` 57 | TrailerUrl2 string `json:"trailerUrl2"` 58 | TrailerUrl3 string `json:"trailerUrl3"` 59 | Issuer string `json:"issuer"` 60 | PrePurchaseStatus string `json:"prePurchaseStatus"` 61 | IsSupportChromeCast string `json:"isSupportChromeCast"` 62 | IsDvdSync string `json:"isDvdSync"` 63 | IsFree string `json:"isFree"` 64 | IsDefaultVideo string `json:"isDefaultVideo"` 65 | CanDownload string `json:"canDownload"` 66 | YValue string `json:"yValue"` 67 | ChromeCastPic string `json:"chromeCastPic"` 68 | MainCategoryDesc string `json:"mainCategoryDesc"` 69 | IsYoutube string `json:"isYoutube"` 70 | YoutubeId string `json:"youtubeId"` 71 | GradeCode string `json:"gradeCode"` 72 | GradeCodeDesc string `json:"gradeCodeDesc"` 73 | TrailerType string `json:"trailerType"` 74 | IsFormal string `json:"isFormal"` 75 | ActorChtList struct { 76 | ActorChtName []string `json:"actorChtName"` 77 | } `json:"actorChtList"` 78 | DirectorChtList struct { 79 | DirectorChtName []string `json:"directorChtName"` 80 | } `json:"directorChtList"` 81 | SubCategoryList struct { 82 | Category []struct { 83 | CategoryCode string `json:"categoryCode"` 84 | CategoryName string `json:"categoryName"` 85 | } `json:"category"` 86 | } `json:"subCategoryList"` 87 | IsMultiLanguage string `json:"isMultiLanguage"` 88 | SubtitleType string `json:"subtitleType"` 89 | MainStill string `json:"mainStill"` 90 | IsDolby string `json:"isDolby"` 91 | IsAVOD string `json:"isAVOD"` 92 | LikeCount string `json:"likeCount"` 93 | DislikeCount string `json:"dislikeCount"` 94 | DisplayStartDate string `json:"displayStartDate"` 95 | StillImageList struct { 96 | StillImage []struct { 97 | StillUrl string `json:"stillUrl"` 98 | StillUrl2 string `json:"stillUrl2"` 99 | StillUrl3 string `json:"stillUrl3"` 100 | IsMasterVision int `json:"isMasterVision"` 101 | } `json:"stillImage"` 102 | } `json:"stillImageList"` 103 | CountryList struct { 104 | CountryName []string `json:"countryName"` 105 | } `json:"countryList"` 106 | OpeningInSec int `json:"openingInSec"` 107 | OpeningOutSec int `json:"openingOutSec"` 108 | EndingInSec int `json:"endingInSec"` 109 | EndingOutSec int `json:"endingOutSec"` 110 | KidMode string `json:"kidMode"` 111 | ShowNewDirectorActor string `json:"showNewDirectorActor"` 112 | DirectorList struct { 113 | Director []struct { 114 | PersonId string `json:"personId"` 115 | ChtName string `json:"chtName"` 116 | ClickAction string `json:"clickAction"` 117 | } `json:"director"` 118 | } `json:"directorList"` 119 | ActorList struct { 120 | Actor []struct { 121 | PersonId string `json:"personId"` 122 | ChtName string `json:"chtName"` 123 | ClickAction string `json:"clickAction"` 124 | EngName string `json:"engName,omitempty"` 125 | } `json:"actor"` 126 | } `json:"actorList"` 127 | GoogleVideoType string `json:"googleVideoType"` 128 | Rating struct { 129 | Type interface{} `json:"type"` 130 | Title interface{} `json:"title"` 131 | Score interface{} `json:"score"` 132 | MaxScore interface{} `json:"maxScore"` 133 | Url interface{} `json:"url"` 134 | } `json:"rating"` 135 | IsLive string `json:"isLive"` 136 | Rank int `json:"rank"` 137 | RankDesc string `json:"rankDesc"` 138 | OffShelfDate string `json:"offShelfDate"` 139 | SeoUpdateTime string `json:"seoUpdateTime"` 140 | IsDolbyVision string `json:"isDolbyVision"` 141 | IsDolbyAtmos string `json:"isDolbyAtmos"` 142 | SeoTitle string `json:"seoTitle"` 143 | AuthorizeStartDate string `json:"authorizeStartDate"` 144 | } `json:"video2"` 145 | PurchaseInfoList struct { 146 | PurchaseInfo []struct { 147 | PurchaseType string `json:"purchaseType"` 148 | PurchaseTitle string `json:"purchaseTitle"` 149 | PurchaseDesc string `json:"purchaseDesc"` 150 | PurchaseCaption string `json:"purchaseCaption"` 151 | CanPurchase string `json:"canPurchase"` 152 | PurchaseInfoStatus string `json:"purchaseInfoStatus"` 153 | PurchaseActionTitle string `json:"purchaseActionTitle"` 154 | } `json:"purchaseInfo"` 155 | } `json:"purchaseInfoList"` 156 | DispWording string `json:"dispWording"` 157 | DispWording2 string `json:"dispWording2"` 158 | SeriesInfo struct { 159 | Id string `json:"id"` 160 | TwmContentId string `json:"twmContentId"` 161 | SeriesContentName string `json:"seriesContentName"` 162 | SeriesType int `json:"seriesType"` 163 | SeasonDispNumber int `json:"seasonDispNumber"` 164 | } `json:"seriesInfo"` 165 | ZeroPriceInfo struct { 166 | IsZeroPrice string `json:"isZeroPrice"` 167 | ZeroPriceType int `json:"zeroPriceType"` 168 | } `json:"zeroPriceInfo"` 169 | VideoPlayRight struct { 170 | PrePurchaseStatus string `json:"prePurchaseStatus"` 171 | HasPlayRight string `json:"hasPlayRight"` 172 | } `json:"videoPlayRight"` 173 | ShowOutStreamAD string `json:"showOutStreamAD"` 174 | UpsellInfo struct { 175 | IsShow string `json:"isShow"` 176 | } `json:"upsellInfo"` 177 | IsMultiScene string `json:"isMultiScene"` 178 | } 179 | type VideoListData struct { 180 | Data string `json:"data"` 181 | Status string `json:"status"` 182 | } 183 | type Base64DDDD struct { 184 | BundleVideoList []struct { 185 | Id string `json:"id"` 186 | DispName string `json:"dispName"` 187 | } `json:"bundleVideoList"` 188 | PackingId string `json:"packingId"` 189 | } 190 | 191 | func GetVideoListData(id string) (b Base64DDDD, err error) { 192 | mv := &VideoListData{} 193 | u := fmt.Sprintf(GetVideoListDataUrl, id) 194 | resp, err := client.Do("get", u, nil) 195 | if err != nil { 196 | return 197 | } 198 | err = json.Unmarshal(resp.Content, &mv) 199 | if err != nil { 200 | return 201 | } 202 | by, err := base64.StdEncoding.DecodeString(mv.Data) 203 | if err != nil { 204 | return 205 | } 206 | err = json.Unmarshal(by, &b) 207 | return 208 | } 209 | func GetVideoInfo(id string, isSeries string) (m Content6, err error) { 210 | mv := &MyVideo{} 211 | if isSeries != "0" { 212 | isSeries = "1" 213 | } 214 | u := fmt.Sprintf(FindMyVideoContent6, id, isSeries) 215 | resp, err := client.Do("get", u, nil) 216 | if err != nil { 217 | return 218 | } 219 | err = json.Unmarshal(resp.Content, &mv) 220 | m = mv.Data 221 | return 222 | } 223 | func GetMateInfo(ctx context.Context, sharerUrl string) (v *server.Data, code int) { 224 | requestId := ctx.Value("requestId").(string) 225 | log.Debugf("site: %s, start GetMateInfo", web) 226 | v = &server.Data{} 227 | us := strings.Split(sharerUrl, "/") 228 | isSeries := us[len(us)-2] 229 | contentId := us[len(us)-1] 230 | m, err := GetVideoInfo(contentId, isSeries) 231 | if err != nil { 232 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 233 | code = 1 234 | return 235 | } 236 | v.SeriesTitle = m.Video2.VideoTitle 237 | v.SeriesId = m.Video2.Id 238 | v.IsVip = m.Video2.IsFree == "N" 239 | v.Extra = map[string]string{ 240 | "videoQuality": m.Video2.VideoQuality, 241 | } 242 | v.IsSeries = isSeries != "0" 243 | if v.IsSeries { 244 | var BundleVideoList Base64DDDD 245 | BundleVideoList, err = GetVideoListData(m.SeriesInfo.TwmContentId) 246 | if err != nil { 247 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 248 | code = 1 249 | return 250 | } 251 | for _, b := range BundleVideoList.BundleVideoList { 252 | vv := &server.Video{ 253 | EpisodeTitle: b.DispName, 254 | EpisodeId: b.Id, 255 | } 256 | v.VideoList = append(v.VideoList, vv) 257 | } 258 | } 259 | return 260 | } 261 | -------------------------------------------------------------------------------- /website/QQTV/proto/basic_data.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package QQTV; 3 | option go_package = "../../QQTV"; 4 | message ShareItem { 5 | optional string share_url = 1; 6 | optional string share_title = 2; 7 | optional string share_subtitle = 3; 8 | optional string share_img_url = 4; 9 | optional string vid = 5; 10 | optional string cid = 6; 11 | optional string share_vertical_img_url = 7; 12 | } 13 | message ReportData { 14 | optional string report_key = 1; 15 | optional string report_params = 2; 16 | } 17 | message Action { 18 | optional string url = 1; 19 | optional ReportData report_data = 2; 20 | } 21 | message MarkLabel { 22 | optional string text = 1; 23 | optional MarkLabelPosition position = 2; 24 | optional string feature_color = 3; 25 | } 26 | message TagLabel { 27 | optional string text = 1; 28 | optional string text_color = 2; 29 | optional string bg_color = 3; 30 | repeated FilterChoiceEntry filter_choice = 4; 31 | message FilterChoiceEntry { 32 | optional string key = 1; 33 | optional string value = 2; 34 | } 35 | } 36 | message Poster { 37 | optional string main_title = 1; 38 | optional string subtitle = 2; 39 | optional string description = 3; 40 | optional string img_url = 4; 41 | repeated MarkLabel mark_label_list = 5; 42 | optional ReportData report_data = 6; 43 | optional Action action = 7; 44 | optional string ad_key = 8; 45 | optional string poster_id = 9; 46 | optional int32 episode_updated_country = 10; 47 | repeated TagLabel tag_label_list = 11; 48 | optional string cid = 12; 49 | optional string pid = 13; 50 | optional string vid = 14; 51 | optional string img_main_color = 15; 52 | optional string tid = 16; 53 | repeated ActorInfo actor_list = 17; 54 | optional string tid_type = 18; 55 | optional string url = 19; 56 | optional string url_outer = 20; 57 | optional int32 expose_limit = 21; 58 | optional string bg_img_url = 22; 59 | optional string logo_img_url = 23; 60 | optional bool title_hide = 24; 61 | optional string nid = 25; 62 | } 63 | message Definition { 64 | optional string value = 1; 65 | optional string short_name = 2; 66 | optional string long_name = 3; 67 | optional bool requires_vip = 4; 68 | } 69 | message VideoItemData { 70 | optional string vid = 1; 71 | optional VideoPaymentType payment_type = 2; 72 | optional Poster poster = 3; 73 | optional int32 skip_start = 4; 74 | optional int32 skip_end = 5; 75 | optional Action action = 6; 76 | optional string title = 7; 77 | optional VideoCopyrightType play_copyright_type = 8; 78 | optional Poster watch_record_poster = 9; 79 | optional ShareItem share_data = 10; 80 | optional string next_play_key = 11; 81 | optional int32 try_watch_duration = 12; 82 | optional string cid = 13; 83 | optional bool pay_preview = 14; 84 | optional float stream_ratio = 15; 85 | optional bool not_record_history = 16; 86 | optional bool disable_external_play = 17; 87 | optional int32 episode_id = 18; 88 | optional bool is_drm = 19; 89 | optional int64 publish_time = 20; 90 | optional int64 play_count = 21; 91 | optional int64 duration = 22; 92 | optional int32 vid_type = 23; 93 | optional int32 status = 24; 94 | optional int32 auto_play_type = 25; 95 | optional int32 age_limit = 26; 96 | optional string episode_id_text = 27; 97 | } 98 | message UserInfo { 99 | optional string vuid = 1; 100 | optional string nick = 2; 101 | optional string avatar = 3; 102 | optional uint64 like_count = 4; 103 | optional uint64 video_count = 5; 104 | optional uint64 follower_count = 6; 105 | optional int32 follow_state = 7; 106 | optional int32 gender = 8; 107 | optional string birthday = 9; 108 | optional string introduction = 10; 109 | optional string email = 11; 110 | } 111 | message ShortVideoPoster { 112 | optional string vid = 1; 113 | optional string cid = 2; 114 | optional Poster poster = 3; 115 | optional Action action = 4; 116 | optional UserInfo user_info = 5; 117 | optional ShareItem share_item = 6; 118 | optional uint64 play_count = 7; 119 | optional int32 duration = 8; 120 | optional bool is_open = 9; 121 | optional bool is_watched = 10; 122 | } 123 | message ABTestConf { 124 | optional string test_id = 1; 125 | repeated MapParamEntry map_param = 2; 126 | message MapParamEntry { 127 | optional string key = 1; 128 | optional string value = 2; 129 | } 130 | } 131 | message ABTestList { 132 | optional string report_ids = 1; 133 | repeated ABTestConf test_list = 2; 134 | } 135 | message PlayCalender { 136 | optional string url = 1; 137 | optional string img_url = 2; 138 | } 139 | message CPInfo { 140 | optional int64 vuid = 1; 141 | optional string nick = 2; 142 | optional string avatar = 3; 143 | optional int32 likecount = 4; 144 | optional int32 videocount = 5; 145 | optional int32 followercount = 6; 146 | optional int32 followstate = 7; 147 | optional Gender gender = 8; 148 | optional string birthday = 9; 149 | optional string introduction = 10; 150 | optional string email = 11; 151 | } 152 | message ExperimentInfo { 153 | optional string experiment_id = 1; 154 | } 155 | message RankInfo { 156 | optional int32 rank = 1; 157 | optional float score = 2; 158 | optional string introduction = 3; 159 | optional string category = 4; 160 | } 161 | message RankPoster { 162 | optional Poster poster = 1; 163 | optional RankInfo rank_info = 2; 164 | } 165 | message SubInfo { 166 | optional string subtitle = 1; 167 | optional VideoOption video_option = 2; 168 | optional LikeInfo like_info = 3; 169 | } 170 | message VideoOption { 171 | optional int64 watched_number = 1; 172 | optional int64 danmu_number = 2; 173 | optional int64 duration = 3; 174 | optional int64 create_time = 4; 175 | optional int64 like_number = 5; 176 | } 177 | message VoteInfo { 178 | optional int64 support_count = 1; 179 | optional string actor_id = 2; 180 | optional string group_id = 3; 181 | optional Action action = 4; 182 | optional string button_content = 5; 183 | } 184 | message ActorInfo { 185 | optional string actor_name = 1; 186 | optional string actor_id = 2; 187 | optional string face_image_url = 3; 188 | optional Action action = 4; 189 | optional VoteInfo vote_info = 5; 190 | } 191 | message ActorList { 192 | optional string title = 1; 193 | repeated ActorInfo actor_info_list = 2; 194 | optional string page_context = 3; 195 | } 196 | message SeasonInfo { 197 | optional string title = 1; 198 | optional string cid = 2; 199 | optional NextPageInfo next_page_info = 3; 200 | optional PlayCalender play_calender = 4; 201 | optional PlayListUiType play_list_ui_type = 5; 202 | optional string detail_info = 6; 203 | } 204 | message VideoDetailBasicInfo { 205 | optional string vid = 1; 206 | optional string cid = 2; 207 | optional int32 video_type = 3; 208 | optional int32 primary_category = 4; 209 | optional int32 area_id = 5; 210 | repeated SeasonInfo season_list = 6; 211 | optional VideoItemData current_video_data = 7; 212 | optional int32 full_episode_count = 8; 213 | optional int32 update_episode = 9; 214 | optional int64 update_time = 10; 215 | optional int64 online_time = 11; 216 | } 217 | message ToolbarItemInfo { 218 | optional bool is_show = 1; 219 | optional bool enable = 2; 220 | } 221 | message WatchList { 222 | optional bool is_show = 1; 223 | optional bool is_watched = 2; 224 | } 225 | message LikeInfo { 226 | optional string data_key = 1; 227 | optional int32 like_count = 2; 228 | optional int32 like_type = 3; 229 | optional string report_key = 4; 230 | optional string report_params = 5; 231 | } 232 | message NextPageInfo { 233 | optional string data_key = 1; 234 | optional string page_context = 2; 235 | optional bool has_next_page = 3; 236 | } 237 | message ColdTagItem { 238 | optional string id = 1; 239 | optional string name = 2; 240 | optional string image_url = 3; 241 | } 242 | message AlbumData { 243 | optional string cid = 1; 244 | optional string title = 2; 245 | optional string pic_url_vertical = 3; 246 | optional string pic_url_horizental = 4; 247 | optional string desc = 5; 248 | optional int32 update_to = 6; 249 | repeated ActorInfo actor_list = 7; 250 | repeated TagLabel tag_label_list = 8; 251 | optional int64 online_time = 9; 252 | } 253 | message StateInfo { 254 | optional bool reserved = 1; 255 | optional bool collected = 2; 256 | } 257 | message AlbumDataInfo { 258 | optional AlbumData album_data = 1; 259 | optional StateInfo state_info = 2; 260 | optional ReportData reportdata = 3; 261 | } 262 | message ButtonMore { 263 | optional bool is_show = 1; 264 | optional Action action = 2; 265 | } 266 | enum MarkLabelPosition { 267 | LOWER_LEFT = 0; 268 | LOWER_RIGHT = 1; 269 | UPPER_RIGHT = 2; 270 | UPPER_LEFT = 3; 271 | } 272 | enum IdType { 273 | ID_TYPE_CID = 0; 274 | ID_TYPE_PID = 1; 275 | ID_TYPE_VID = 2; 276 | } 277 | enum VideoPaymentType { 278 | UNKNOWN = 0; 279 | VOD = 4; 280 | VOD_VIP_EXMT = 5; 281 | VIP_ONLY = 6; 282 | FREE_FOR_ALL = 8; 283 | SINGLE_PAY = 7; 284 | VOD_AHEAD = 12; 285 | } 286 | enum VideoCopyrightType { 287 | PLAY_IN_WEBVIEW = 0; 288 | PLAY_IN_APP = 1; 289 | PLAY_IN_WEBVIEW_WITH_STMT = 101; 290 | } 291 | enum Gender { 292 | GENDER_MALE = 0; 293 | GENDER_FEMALE = 1; 294 | GENDER_OTHER = 2; 295 | } 296 | enum PlayListUiType { 297 | PLAY_LSIT_TYPE_TEXT = 0; 298 | PLAY_LIST_TYPE_POSTER = 1; 299 | } 300 | enum LiveNumberType { 301 | TYPE_UNKNOW = 0; 302 | TYPE_ONLINE = 1; 303 | TYPE_RESERVE = 2; 304 | } 305 | enum LoadType { 306 | LOAD_TYPE_MANUAL = 0; 307 | LOAD_TYPE_TIMEOUT = 1; 308 | LOAD_TYPE_PRELOAD = 2; 309 | LOAD_TYPE_NEXTPAGE = 3; 310 | } 311 | enum SearchPosterType { 312 | TYPE_SMALL = 0; 313 | TYPE_LARGE = 1; 314 | TYPE_POSTER = 2; 315 | } 316 | -------------------------------------------------------------------------------- /website/MIGU/mg.go: -------------------------------------------------------------------------------- 1 | package MIGU 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "strconv" 12 | "strings" 13 | "trpc.group/trpc-go/trpc-go/log" 14 | ) 15 | 16 | var headers = map[string]string{ 17 | "APP-VERSION-CODE": "81170308", 18 | "appVersion": "0", 19 | "areaId": "280", 20 | "carrierCode": "CT", 21 | "channel": "0148", 22 | "clientCityId": "0280", 23 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36", 24 | } 25 | var ( 26 | web = "migu" 27 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 28 | ) 29 | 30 | type MgRsp struct { 31 | Code int `json:"code"` 32 | Message string `json:"message"` 33 | Body struct { 34 | Data struct { 35 | Actor string `json:"actor"` 36 | Area string `json:"area"` 37 | AssetID string `json:"assetID"` 38 | EpsAssetID string `json:"epsAssetID"` 39 | EpsID string `json:"epsID"` 40 | Year string `json:"year"` 41 | ContentStyle string `json:"contentStyle"` 42 | Director string `json:"director"` 43 | Detail string `json:"detail"` 44 | EpsCount int `json:"epsCount"` 45 | Name string `json:"name"` 46 | ProgramType string `json:"programType"` 47 | Score string `json:"score"` 48 | PublishTime string `json:"publishTime"` 49 | StateTime string `json:"stateTime"` 50 | UpdateEP string `json:"updateEP"` 51 | Label4K string `json:"label_4K"` 52 | Tip struct { 53 | Code string `json:"code"` 54 | Msg string `json:"msg"` 55 | } `json:"tip"` 56 | DownloadTip struct { 57 | Code string `json:"code"` 58 | Msg string `json:"msg"` 59 | } `json:"downloadTip"` 60 | Playing struct { 61 | PID string `json:"pID"` 62 | Name string `json:"name"` 63 | Duration string `json:"duration"` 64 | Index string `json:"index"` 65 | LimitFreeTip struct { 66 | FreeLimitDates []struct { 67 | BeginDate string `json:"beginDate"` 68 | EndDate string `json:"endDate"` 69 | } `json:"freeLimitDates"` 70 | } `json:"limitFreeTip"` 71 | LimitedTimeTips []struct { 72 | LimitedTime []struct { 73 | BeginDate string `json:"beginDate"` 74 | EndDate string `json:"endDate"` 75 | } `json:"limitedTime"` 76 | } `json:"limitedTimeTips"` 77 | Label4K string `json:"label_4K"` 78 | ReqType string `json:"reqType"` 79 | CdnData string `json:"cdnData"` 80 | } `json:"playing"` 81 | Datas []struct { 82 | LimitFreeTip struct { 83 | FreeLimitDates []struct { 84 | BeginDate string `json:"beginDate"` 85 | EndDate string `json:"endDate"` 86 | } `json:"freeLimitDates"` 87 | } `json:"limitFreeTip"` 88 | LimitedTimeTips []struct { 89 | LimitedTime []struct { 90 | BeginDate string `json:"beginDate"` 91 | EndDate string `json:"endDate"` 92 | } `json:"limitedTime"` 93 | } `json:"limitedTimeTips"` 94 | Tip struct { 95 | Code string `json:"code"` 96 | Msg string `json:"msg"` 97 | } `json:"tip"` 98 | DownloadTip struct { 99 | Code string `json:"code"` 100 | Msg string `json:"msg"` 101 | } `json:"downloadTip"` 102 | Duration string `json:"duration"` 103 | Name string `json:"name"` 104 | Detail string `json:"detail"` 105 | PID string `json:"pID"` 106 | ProgramTypeV2 string `json:"programTypeV2"` 107 | Pics struct { 108 | LowResolutionH string `json:"lowResolutionH"` 109 | LowResolutionV string `json:"lowResolutionV"` 110 | LowResolutionV34 string `json:"lowResolutionV34"` 111 | HighResolutionH string `json:"highResolutionH"` 112 | HighResolutionV string `json:"highResolutionV"` 113 | HighResolutionV34 string `json:"highResolutionV34"` 114 | } `json:"pics"` 115 | H5Pics struct { 116 | LowResolutionH string `json:"lowResolutionH"` 117 | LowResolutionV string `json:"lowResolutionV"` 118 | LowResolutionV34 string `json:"lowResolutionV34"` 119 | HighResolutionH string `json:"highResolutionH"` 120 | HighResolutionV string `json:"highResolutionV"` 121 | HighResolutionV34 string `json:"highResolutionV34"` 122 | } `json:"h5pics"` 123 | AssetID string `json:"assetID"` 124 | Label4K string `json:"label_4K"` 125 | VideoType string `json:"videoType"` 126 | Resolution struct { 127 | MediaWidth int `json:"mediaWidth"` 128 | MediaHeight int `json:"mediaHeight"` 129 | } `json:"resolution"` 130 | Index string `json:"index"` 131 | CopyRightVo struct { 132 | Terminal string `json:"terminal"` 133 | Area string `json:"area"` 134 | BeginDate string `json:"beginDate"` 135 | EndDate string `json:"endDate"` 136 | Way string `json:"way"` 137 | } `json:"copyRightVo"` 138 | Way string `json:"way"` 139 | DisplayType string `json:"displayType"` 140 | IsUFC string `json:"isUFC"` 141 | IsPrevue string `json:"isPrevue"` 142 | ReqType string `json:"reqType"` 143 | CdnData string `json:"cdnData"` 144 | IsHDRVivid string `json:"isHDRVivid"` 145 | ContentStatus string `json:"contentStatus"` 146 | KEYWORDS string `json:"KEYWORDS"` 147 | } `json:"datas"` 148 | Star []struct { 149 | Career string `json:"career"` 150 | Img string `json:"img"` 151 | Name string `json:"name"` 152 | StarId string `json:"starId"` 153 | Action struct { 154 | Type string `json:"type"` 155 | IosMinVersion string `json:"iosMinVersion"` 156 | AndroidMinVersion string `json:"androidMinVersion"` 157 | Params struct { 158 | FrameID string `json:"frameID"` 159 | PageID string `json:"pageID"` 160 | ContentID string `json:"contentID"` 161 | Location string `json:"location"` 162 | Extra struct { 163 | StarID string `json:"starID"` 164 | } `json:"extra"` 165 | } `json:"params"` 166 | } `json:"action"` 167 | } `json:"star"` 168 | PrdPackId string `json:"prdPackId"` 169 | Pics struct { 170 | LowResolutionH string `json:"lowResolutionH"` 171 | LowResolutionV string `json:"lowResolutionV"` 172 | LowResolutionV34 string `json:"lowResolutionV34"` 173 | HighResolutionH string `json:"highResolutionH"` 174 | HighResolutionV string `json:"highResolutionV"` 175 | HighResolutionV34 string `json:"highResolutionV34"` 176 | } `json:"pics"` 177 | Way string `json:"way"` 178 | TotalPage string `json:"totalPage"` 179 | TotalCount string `json:"totalCount"` 180 | PageSize string `json:"pageSize"` 181 | PricingStage string `json:"pricing_stage"` 182 | H5Pics struct { 183 | LowResolutionH string `json:"lowResolutionH"` 184 | LowResolutionV string `json:"lowResolutionV"` 185 | LowResolutionV34 string `json:"lowResolutionV34"` 186 | HighResolutionH string `json:"highResolutionH"` 187 | HighResolutionV string `json:"highResolutionV"` 188 | HighResolutionV34 string `json:"highResolutionV34"` 189 | } `json:"h5pics"` 190 | MpId string `json:"mpId"` 191 | ProgramTypeV2 string `json:"programTypeV2"` 192 | CopyRightVo struct { 193 | Terminal string `json:"terminal"` 194 | Area string `json:"area"` 195 | BeginDate string `json:"beginDate"` 196 | EndDate string `json:"endDate"` 197 | Way string `json:"way"` 198 | } `json:"copyRightVo"` 199 | Directors []struct { 200 | Img string `json:"img"` 201 | Name string `json:"name"` 202 | StarId string `json:"starId"` 203 | Action struct { 204 | Type string `json:"type"` 205 | IosMinVersion string `json:"iosMinVersion"` 206 | AndroidMinVersion string `json:"androidMinVersion"` 207 | Params struct { 208 | FrameID string `json:"frameID"` 209 | PageID string `json:"pageID"` 210 | ContentID string `json:"contentID"` 211 | Location string `json:"location"` 212 | Extra struct { 213 | StarID string `json:"starID"` 214 | } `json:"extra"` 215 | } `json:"params"` 216 | } `json:"action"` 217 | } `json:"directors"` 218 | VideoType string `json:"videoType"` 219 | DisplayType string `json:"displayType"` 220 | IsUFC string `json:"isUFC"` 221 | Recommendation string `json:"recommendation"` 222 | ProgramForm string `json:"programForm"` 223 | Language string `json:"language"` 224 | IsHDRVivid string `json:"isHDRVivid"` 225 | IsEnd string `json:"isEnd"` 226 | KEYWORDS string `json:"KEYWORDS"` 227 | } `json:"data"` 228 | } `json:"body"` 229 | TimeStamp int64 `json:"timeStamp"` 230 | TimeTraceVO struct { 231 | RequestTime string `json:"requestTime"` 232 | ResponseTime string `json:"responseTime"` 233 | } `json:"timeTraceVO"` 234 | } 235 | 236 | func GetMateInfo(ctx context.Context, sharerUrl string) (r *server.Data, code int) { 237 | requestId := ctx.Value("requestId").(string) 238 | log.Debugf("site: %s, start GetMateInfo", web) 239 | r = &server.Data{} 240 | b, _, _ := strings.Cut(sharerUrl, "?") 241 | bs := strings.Split(b, "/") 242 | cid := bs[len(bs)-1] 243 | u := fmt.Sprintf("https://v2-sc.miguvideo.com/program/v3/cont/content-info/%s", cid) 244 | Req := url.NewRequest() 245 | Req.Headers = url.NewHeaders() 246 | for k, v := range headers { 247 | Req.Headers.Add(k, v) 248 | } 249 | re, err := client.Do("get", u, Req) 250 | if err != nil { 251 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 252 | return 253 | } 254 | var rsp MgRsp 255 | err = json.Unmarshal(re.Content, &rsp) 256 | if err != nil { 257 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 258 | code = 1 259 | return 260 | } 261 | if rsp.Code != 200 { 262 | err = fmt.Errorf(rsp.Message) 263 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 264 | code = 1 265 | return 266 | } 267 | r.SeriesTitle = rsp.Body.Data.Name 268 | r.SeriesId = rsp.Body.Data.AssetID 269 | if rsp.Body.Data.Datas != nil { 270 | r.IsSeries = true 271 | for _, v := range rsp.Body.Data.Datas { 272 | e, _ := strconv.Atoi(v.Index) 273 | r.VideoList = append(r.VideoList, &server.Video{ 274 | EpisodeId: v.PID, 275 | EpisodeTitle: v.Name, 276 | Extra: map[string]string{ 277 | "label_4K": rsp.Body.Data.Label4K, 278 | "HDRVivid": rsp.Body.Data.IsHDRVivid, 279 | }, 280 | Episode: uint32(e), 281 | IsTrailer: v.IsPrevue == "1", 282 | IsVip: v.Tip.Code == "VIP" || v.Tip.Code == "USE_TICKET", 283 | }) 284 | } 285 | } else { 286 | r.IsSeries = false 287 | r.VideoList = append(r.VideoList, &server.Video{ 288 | EpisodeId: rsp.Body.Data.Playing.PID, 289 | EpisodeTitle: rsp.Body.Data.Playing.Name, 290 | Extra: map[string]string{ 291 | "label_4K": rsp.Body.Data.Label4K, 292 | "HDRVivid": rsp.Body.Data.IsHDRVivid, 293 | }, 294 | }) 295 | } 296 | return 297 | } 298 | -------------------------------------------------------------------------------- /website/MGTV/mgtv.go: -------------------------------------------------------------------------------- 1 | package MGTV 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Tontonnow/ddddhmlist/config" 8 | "github.com/Tontonnow/ddddhmlist/server" 9 | "github.com/Tontonnow/ddddhmlist/utils/sesssion" 10 | "github.com/wangluozhe/requests/url" 11 | "strings" 12 | "trpc.group/trpc-go/trpc-go/log" 13 | ) 14 | 15 | var ( 16 | web = "MGTV" 17 | params = map[string]interface{}{ 18 | "uuid": "mgtvmac", 19 | "version": "6.2.511.390.3.DANGBEI_TVAPP.0.0_Release", 20 | "device_id": "0", 21 | "mac_id": strings.Replace(config.Conf.Mac, ":", "-", -1), 22 | } 23 | headers = map[string]string{ 24 | "Accept-Encoding": "gzip", 25 | "Connection": "Keep-Alive", 26 | "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 7.1.2; EU9-hi3716M Build/N2G48C)", 27 | } 28 | api = "https://ott.bz.mgtv.com/ott/v2/video/info?uuid=mgtvmac&version=9&device_id=0&mac_id=" + strings.Replace(config.Conf.Mac, ":", "-", -1) + "&partId=" 29 | client = sesssion.NewClient(config.Conf.WebConfig[web]) 30 | ) 31 | 32 | type MgRsp struct { 33 | Msg string `json:"msg"` 34 | Code int `json:"code"` 35 | Data MgData `json:"data"` 36 | Seqid string `json:"seqid"` 37 | } 38 | type MgData struct { 39 | Total int `json:"total"` 40 | PageNo int `json:"pageNo"` 41 | TotalPage int `json:"totalPage"` 42 | PageSize int `json:"pageSize"` 43 | ExtraCount int `json:"extraCount"` 44 | Rows []struct { 45 | ClipId string `json:"clipId"` 46 | CornerLabelStyle struct { 47 | Color string `json:"color"` 48 | Font string `json:"font"` 49 | } `json:"cornerLabelStyle"` 50 | Desc string `json:"desc"` 51 | Duration int `json:"duration"` 52 | Image string `json:"image"` 53 | Index int `json:"index"` 54 | IsPositive int `json:"isPositive"` 55 | Name string `json:"name"` 56 | OttFileFormatDuration string `json:"ottFileFormatDuration"` 57 | PlId string `json:"plId"` 58 | Title string `json:"title"` 59 | Type int `json:"type"` 60 | VideoId string `json:"videoId"` 61 | } `json:"rows"` 62 | AbrConfig struct { 63 | CheckInterval int `json:"checkInterval"` 64 | SelectAbr bool `json:"selectAbr"` 65 | EnhanceAPI string `json:"enhanceAPI"` 66 | Fts string `json:"fts"` 67 | SwithLimit struct { 68 | Up int `json:"up"` 69 | Down int `json:"down"` 70 | } `json:"swithLimit"` 71 | EnableAbr bool `json:"enableAbr"` 72 | MaxIdleQuality int `json:"maxIdleQuality"` 73 | MinCheckInterval int `json:"minCheckInterval"` 74 | MinIdleQuality int `json:"minIdleQuality"` 75 | Algorithm int `json:"algorithm"` 76 | } `json:"abrConfig"` 77 | AbrSources []interface{} `json:"abrSources"` 78 | Attach struct { 79 | Defs []struct { 80 | StandardName string `json:"standardName"` 81 | Framerate string `json:"framerate"` 82 | Name string `json:"name"` 83 | DefLevel int `json:"defLevel"` 84 | Filebitrate string `json:"filebitrate"` 85 | Type int `json:"type"` 86 | Playable int `json:"playable"` 87 | } `json:"defs"` 88 | Default int `json:"default"` 89 | Force int `json:"force"` 90 | Displays []int `json:"displays"` 91 | Points []struct { 92 | PointTitle string `json:"pointTitle"` 93 | PointPic string `json:"pointPic"` 94 | PointStart int `json:"pointStart"` 95 | PartPointId int `json:"partPointId"` 96 | PointId int `json:"pointId"` 97 | PartId int `json:"partId"` 98 | HasMppFile int `json:"hasMppFile"` 99 | PointType int `json:"pointType"` 100 | HasOttFile int `json:"hasOttFile"` 101 | ImgHash string `json:"imgHash"` 102 | } `json:"points"` 103 | } `json:"attach"` 104 | CategoryList []struct { 105 | Code string `json:"code"` 106 | Datatype int `json:"datatype"` 107 | Isrefresh int `json:"isrefresh"` 108 | More string `json:"more"` 109 | ObjectType int `json:"objectType"` 110 | Playorder int `json:"playorder"` 111 | Position int `json:"position"` 112 | Showtype int `json:"showtype"` 113 | Title string `json:"title"` 114 | Url string `json:"url"` 115 | } `json:"categoryList"` 116 | ClipId string `json:"clipId"` 117 | ClipImage string `json:"clipImage"` 118 | ClipName string `json:"clipName"` 119 | ClipStatus string `json:"clipStatus"` 120 | ClipVerticalImage string `json:"clipVerticalImage"` 121 | Detail []string `json:"detail"` 122 | Duration int `json:"duration"` 123 | FitAge string `json:"fitAge"` 124 | FstlvlId string `json:"fstlvlId"` 125 | FstlvlName string `json:"fstlvlName"` 126 | Index int `json:"index"` 127 | Info string `json:"info"` 128 | IsIntact int `json:"isIntact"` 129 | KeepPlay struct { 130 | Duration int `json:"duration"` 131 | Info string `json:"info"` 132 | Vid string `json:"vid"` 133 | WatchTime int `json:"watchTime"` 134 | } `json:"keepPlay"` 135 | KeepPlayType int `json:"keepPlayType"` 136 | Kind string `json:"kind"` 137 | ModuleDetailInfo struct { 138 | UModuleId string `json:"uModuleId"` 139 | ProgressBarEffect string `json:"progressBarEffect"` 140 | WhiteBgUrl string `json:"whiteBgUrl"` 141 | ProgressBarColor string `json:"progressBarColor"` 142 | ChannelAllModules []struct { 143 | ModuleType string `json:"moduleType"` 144 | OrderNum int `json:"orderNum"` 145 | ModuleId string `json:"moduleId"` 146 | } `json:"channelAllModules"` 147 | HitType string `json:"hitType"` 148 | QiJingBrandImg string `json:"qiJingBrandImg"` 149 | BlackBgUrl string `json:"blackBgUrl"` 150 | VclassId string `json:"vclassId"` 151 | QiJingFeatureFlag string `json:"qiJingFeatureFlag"` 152 | RectangleProgressBarLogo string `json:"rectangleProgressBarLogo"` 153 | CategoryList []struct { 154 | Datatype string `json:"datatype"` 155 | Position string `json:"position"` 156 | } `json:"categoryList"` 157 | TopVclassId string `json:"topVclassId"` 158 | ProgressBarLogoUrl string `json:"progressBarLogoUrl"` 159 | } `json:"moduleDetailInfo"` 160 | PlId string `json:"plId"` 161 | PlImage string `json:"plImage"` 162 | PlName string `json:"plName"` 163 | PlVerticalImage string `json:"plVerticalImage"` 164 | Play string `json:"play"` 165 | PublishYear string `json:"publishYear"` 166 | RecState string `json:"recState"` 167 | RecommendInfo struct { 168 | ModuleNum string `json:"moduleNum"` 169 | ModuleName string `json:"moduleName"` 170 | ModuleId string `json:"moduleId"` 171 | } `json:"recommendInfo"` 172 | RelatedPlay interface{} `json:"relatedPlay"` 173 | Serialno string `json:"serialno"` 174 | SerieTabs []struct { 175 | ClipID string `json:"clipId"` 176 | Current int `json:"current"` 177 | SeasonID string `json:"seasonId"` 178 | Title string `json:"title"` 179 | URL string `json:"url"` 180 | } `json:"serieTabs"` 181 | SeriesId string `json:"seriesId"` 182 | ShowMode int `json:"showMode"` 183 | ShowTitle string `json:"showTitle"` 184 | TotalNumber string `json:"totalNumber"` 185 | TotalSize int `json:"totalSize"` 186 | UpdateDesc string `json:"updateDesc"` 187 | UpdateInfo string `json:"updateInfo"` 188 | VclassId string `json:"vclassId"` 189 | VideoId string `json:"videoId"` 190 | VideoImage string `json:"videoImage"` 191 | VideoName string `json:"videoName"` 192 | VideoVerticalImage string `json:"videoVerticalImage"` 193 | VipInfo struct { 194 | Preview int `json:"preview"` 195 | VipDefs []int `json:"vip_defs"` 196 | PRange int `json:"p_range"` 197 | ChargeFirms []interface{} `json:"charge_firms"` 198 | Hdcp int `json:"hdcp"` 199 | Mark int `json:"mark"` 200 | } `json:"vipInfo"` 201 | VipInfoOtt struct { 202 | Preview int `json:"preview"` 203 | VipDefs []int `json:"vip_defs"` 204 | PRange int `json:"p_range"` 205 | ChargeFirms []interface{} `json:"charge_firms"` 206 | Hdcp int `json:"hdcp"` 207 | Mark int `json:"mark"` 208 | } `json:"vipInfoOtt"` 209 | VipMark struct { 210 | VipDefs []int `json:"vip_defs"` 211 | ChargeFirms []interface{} `json:"charge_firms"` 212 | Mark int `json:"mark"` 213 | } `json:"vipMark"` 214 | VipMarkOtt struct { 215 | VipDefs []int `json:"vip_defs"` 216 | ChargeFirms []interface{} `json:"charge_firms"` 217 | Mark int `json:"mark"` 218 | } `json:"vipMarkOtt"` 219 | WatchTime int `json:"watchTime"` 220 | } 221 | 222 | func doRequest(u string, data map[string]interface{}) (re MgData, err error) { 223 | Req := url.NewRequest() 224 | Req.Headers = url.NewHeaders() 225 | for k, v := range headers { 226 | Req.Headers.Add(k, v) 227 | } 228 | Req.Json = data 229 | r, err := client.Do("get", u, Req) 230 | if err != nil { 231 | log.Error("MGTV", "doRequest", err) 232 | return 233 | } 234 | var rsp MgRsp 235 | err = json.Unmarshal(r.Content, &rsp) 236 | if err != nil { 237 | log.Error("MGTV", "doRequest", err) 238 | return 239 | } 240 | if rsp.Code != 200 { 241 | err = fmt.Errorf(rsp.Msg) 242 | log.Error("MGTV", "doRequest", err) 243 | return 244 | } 245 | re = rsp.Data 246 | return 247 | } 248 | 249 | func GetMateInfo(ctx context.Context, sharerUrl string) (ret *server.Data, code int) { 250 | requestId := ctx.Value("requestId").(string) 251 | log.Debugf("site: %s, start GetMateInfo", web) 252 | ret = &server.Data{} 253 | var pageSize, pageNo int 254 | pageSize = 100 255 | pageNo = 1 256 | b, _, _ := strings.Cut(sharerUrl, ".html") 257 | bs := strings.Split(b, "/") 258 | cid := bs[len(bs)-1] 259 | u := api + cid 260 | r, err := doRequest(u, nil) 261 | if err != nil { 262 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 263 | code = 1 264 | return 265 | } 266 | ret.SeriesTitle = r.ClipName 267 | ret.SeriesId = r.ClipId 268 | categoryList := r.CategoryList 269 | /*如果只需要多季正片可以取serieTabs 270 | for _, v := range r.SerieTabs { 271 | u := v.URL + "&pageSize=" + fmt.Sprint(pageSize) + "&pageNo=" + fmt.Sprint(pageNo) 272 | } 273 | */ 274 | for _, v := range categoryList { 275 | if v.Code == "formal" { //默认只取正片 其余的可以自行添加 可能需要修改结构体 276 | u := v.Url + "&pageSize=" + fmt.Sprint(pageSize) + "&pageNo=" + fmt.Sprint(pageNo) 277 | r, err = doRequest(u, nil) 278 | if err != nil { 279 | log.Errorf("site: %s, requestId: %s, error: %v, url: %s", web, requestId, err, sharerUrl) 280 | code = 1 281 | return 282 | } 283 | ret.Extra = map[string]string{ 284 | "total": fmt.Sprint(r.Total), 285 | "totalPage": fmt.Sprint(r.TotalPage)} 286 | 287 | for _, v := range r.Rows { 288 | if v.Type == 8 || v.VideoId == "" { 289 | continue 290 | } 291 | ret.VideoList = append(ret.VideoList, &server.Video{ 292 | EpisodeTitle: v.Name, 293 | EpisodeId: v.VideoId, 294 | IsVip: v.CornerLabelStyle.Font == "VIP", 295 | IsTrailer: v.Type != 1, 296 | }) 297 | } 298 | } 299 | } 300 | return 301 | } 302 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddddhmlist 2 | 3 | 获取流媒体视频列表信息 4 | 5 | [![img](https://img.shields.io/github/stars/Tontonnow/ddddhmlist?label=%E7%82%B9%E8%B5%9E)](https://github.com/Tontonnow/ddddhmlist) [![img](https://img.shields.io/github/last-commit/Tontonnow/ddddhmlist?label=%E6%9C%80%E8%BF%91%E6%8F%90%E4%BA%A4)](https://github.com/Tontonnow/ddddhmlist) [![img](https://img.shields.io/github/release/Tontonnow/ddddhmlist?label=%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC)](https://github.com/Tontonnow/ddddhmlist/releases) [![img](https://img.shields.io/github/license/Tontonnow/ddddhmlist?label=%E8%AE%B8%E5%8F%AF%E8%AF%81)](https://github.com/Tontonnow/ddddhmlist) [![img](https://img.shields.io/github/downloads/Tontonnow/ddddhmlist/total?label=%E4%B8%8B%E8%BD%BD%E9%87%8F)](https://github.com/Tontonnow/ddddhmlist/releases) [![img](https://img.shields.io/github/contributors/Tontonnow/ddddhmlist)](https://github.com/Tontonnow/ddddhmlist/graphs/contributors) 6 | 7 | 8 | * [ddddhmlist](#ddddhmlist) 9 | * [介绍](#介绍) 10 | * [文件目录说明](#文件目录说明) 11 | * [使用方法](#使用方法) 12 | * [配置文件说明](#配置文件说明) 13 | * [API](#api) 14 | * [获取视频列表](#获取视频列表) 15 | * [如何参与贡献](#如何参与贡献) 16 | * [注意事项](#注意事项) 17 | * [已知问题](#已知问题) 18 | * [更新日志](#更新日志) 19 | * [2024-04-06](#2024-04-06) 20 | * [更新计划](#更新计划) 21 | * [提问交流](#提问交流) 22 | 23 | 24 | 25 | ## 介绍 26 | 27 | 本项目是一个获取流媒体视频列表信息的工具。 28 | 29 | 主要支持中国各大流媒体平台点播视频的获取,个人上传的视频不支持(不测试,实际自行测试)。 30 | 31 | 接口主要使用app和TV端的接口,绝大部分不需要额外参数,不保证长期有效,如有问题请提交issue或者pr。 32 | 33 | 目前支持以下网站: 34 | 35 | | site | url | Status | 36 | |------------------------------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 37 | | [Bilibili](website%2FBilibili) | https://www.bilibili.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/Bilibili.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/Bilibili.yml) | 38 | | [Friday](website%2FFriday) | https://video.friday.tw | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/Friday.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/Friday.yml) | 39 | | [Hami](website%2FHami) | https://hamivideo.hinet.net | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/Hami.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/Hami.yml) | 40 | | [iqy](website%2FIQ%2Fiqy.go) | https://www.iqiyi.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/IQ.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/IQ.yml) | 41 | | [iq](website%2FIQ%2Fiq.go) | https://www.iq.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/IQ.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/IQ.yml) | 42 | | [KKTV](website%2FKKTV) | https://kktv.me | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/KKTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/KKTV.yml) | 43 | | [LETV](website%2FLETV) | https://www.le.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/LETV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/LETV.yml) | 44 | | [Litv](website%2FLitv) | https://www.litv.tv | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/Litv.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/Litv.yml) | 45 | | [MGTV](website%2FMGTV) | https://www.mgtv.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/MGTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/MGTV.yml) | 46 | | [MIGU](website%2FMIGU) | https://m.miguvideo.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/MIGU.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/MIGU.yml) | 47 | | [MytvSuper](website%2FMytvSuper) | https://www.mytvsuper.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/MytvSuper.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/MytvSuper.yml) | 48 | | [MyVideo](website%2FMyVideo) | https://www.myvideo.net.tw | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/MyVideo.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/MyVideo.yml) | 49 | | [TXTV](website%2FQQTV%2FTXTV.go) | https://v.qq.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/QQTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/QQTV.yml) | 50 | | [WETV](website%2FQQTV%2FWETV.go) | https://wetv.vip | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/QQTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/QQTV.yml) | 51 | | [VIKI](website%2FVIKI) | https://www.viki.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/VIKI.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/VIKI.yml) | 52 | | [VIU](website%2FVIU) | https://www.viu.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/VIU.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/VIU.yml) | 53 | | [XiGUA](website%2FXiGUA) | https://www.ixigua.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/XiGUA.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/XiGUA.yml) | 54 | | [yangshipin](website%2Fyangshipin) | https://www.yangshipin.cn | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/yangshipin.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/yangshipin.yml) | 55 | | [YOUKUTV](website%2FYOUKUTV) | https://v.youku.com | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/YOUKUTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/YOUKUTV.yml) | 56 | | [YOUKUTV](website%2FYOUKUTV) | https://www.youku.tv | ![https://github.com/Tontonnow/ddddhmlist/actions/workflows/YOUKUTV.yml](https://img.shields.io/github/actions/workflow/status/Tontonnow/ddddhmlist/YOUKUTV.yml) | 57 | | | | | 58 | 59 | ## 文件目录说明 60 | 61 | ``` 62 | 63 | │ config.yaml 64 | │ trpc_go.yaml //这两个配置文件必须有,注意要和软件运行目录放一起 65 | │ go.mod 66 | │ go.sum 67 | │ LICENSE.txt 68 | │ README.md 69 | ├─.github 70 | │ └─workflows //在线自动test或者编译,建议fork后自行运行或修改,在线测试不一定准确 71 | ├─cmd 72 | │ ├─client 73 | │ │ a.http 74 | │ │ a.sh 75 | │ │ client.go 76 | │ │ 77 | │ └─server 78 | │ a.sh //win 下交叉编译命令 shell 79 | │ app.go 80 | │ 81 | ├─config 82 | │ config.go 83 | │ 84 | ├─img 85 | │ img.png 86 | │ 87 | ├─server 88 | │ │ data.go 89 | │ │ ddddlist.pb.go 90 | │ │ 91 | │ ├─proto 92 | │ │ ddddlist.proto //proto文件 很重要 93 | │ │ ddddlist.sh //编译trpc代码命令行 94 | │ │ 95 | │ └─trpc 96 | │ ddddlist.trpc.go 97 | │ server.go 98 | │ 99 | ├─tmp 100 | │ └─log //运行日志存放目录 101 | ├─utils 102 | │ │ tool.go 103 | │ │ 104 | │ ├─jwt 105 | │ │ jwt.go 106 | │ │ jwt_test.go 107 | │ │ 108 | │ └─sesssion 109 | │ session.go 110 | │ 111 | └─website 112 | │ web.go 113 | │ 114 | ├─Bilibili 115 | │ b.go //主体,建议自行修改,增加字段优化代码 116 | │ b_test.go //test文件 修改代码后建议先test 117 | │ 118 | ├─Friday 119 | │ Friday.go 120 | │ Friday_test.go 121 | │ 122 | ├─Hami 123 | │ hami.go 124 | │ hami_test.go 125 | │ 126 | ├─IQ 127 | │ iq.go 128 | │ iqy.go 129 | │ iqy_test.go 130 | │ iq_test.go 131 | │ 132 | ├─KKTV 133 | │ kktv.go 134 | │ kktv_test.go 135 | │ 136 | ├─LETV 137 | │ letv.go 138 | │ letv_test.go 139 | │ 140 | ├─Litv 141 | │ litv.go 142 | │ litv_test.go 143 | │ 144 | ├─MGTV 145 | │ mgtv.go 146 | │ mgtv_test.go 147 | │ 148 | ├─MIGU 149 | │ mg.go 150 | │ mg_test.go 151 | │ 152 | ├─MytvSuper 153 | │ mytvsuper.go 154 | │ mytvsuper_test.go 155 | │ 156 | ├─MyVideo 157 | │ myvideo.go 158 | │ myvideo_test.go 159 | │ 160 | ├─QQTV 161 | │ │ basic_data.pb.go 162 | │ │ env.pb.go 163 | │ │ feed_data.pb.go 164 | │ │ request_base.pb.go 165 | │ │ trpc_video_detail_list.pb.go 166 | │ │ TXTV.go 167 | │ │ TXTV_test.go 168 | │ │ WETV.go 169 | │ │ wetv.pb.go 170 | │ │ 171 | │ └─proto //WETV 的trpc pb文件,自己实现了trpc组包,建议阅读trpc文档,自行重新优化 172 | │ basic_data.proto 173 | │ env.proto 174 | │ feed_data.proto 175 | │ request_base.proto 176 | │ trpc_video_detail_list.proto 177 | │ wetv.proto 178 | │ 179 | ├─VIKI 180 | │ viki.go 181 | │ viki_test.go 182 | │ 183 | ├─VIU 184 | │ viu.go 185 | │ viu_test.go 186 | │ 187 | ├─XiGUA 188 | │ xg.go 189 | │ xg_test.go 190 | │ 191 | ├─yangshipin 192 | │ │ yangshipin.pb.go 193 | │ │ ysp.go 194 | │ │ ysp_test.go 195 | │ │ 196 | │ ├─proto 197 | │ │ yangshipin.proto //web 端 198 | │ │ 199 | │ └─tars //app 端 协议暂未开源 建议自行研究 tx jce(tars) 200 | │ ONATVDetailsVideoSquareListItem.tars 201 | │ RequestCommand.tars 202 | │ ResponseCommand.tars 203 | │ VideoDetailsRequest.tars 204 | │ VideoDetailsResponse.tars 205 | │ 206 | └─YOUKUTV 207 | yk.go 208 | yk_test.go 209 | 210 | ``` 211 | 212 | ## 使用方法 213 | 214 | 安装go,并设置好环境变量,然后执行以下命令 215 | 216 | ``` 217 | git clone https://github.com/Tontonnow/ddddhmlist.git 218 | 219 | 修改config.yaml(可选,主要是部分网站需要代理) 220 | 221 | cd ddddhmlist 222 | go run ./cmd/server 223 | ``` 224 | 225 | 然后访问http://127.0.0.1:8001/api/v1/ddddlist?url=https://v.youku.com/v_show/id_XNTk1MjQxNjQzMg==.html 226 | 227 | ![img.png](img/img.png) 228 | ### 配置文件说明 229 | 230 | [config.yaml](config.yaml) 程序配置文件 目前就一个proxy字段有用,其他的暂时没写 231 | 232 | [tree.txt](tree.txt) trpc的配置文件,自行参考trpc[文档](https://github.com/trpc-group/trpc-go/blob/main/README.zh_CN.md) 233 | 234 | 235 | 236 | 237 | 238 | 239 | ## API 240 | ### 获取视频列表 241 | 242 | > ip:port/api/v1/ddddlist?url=xxx 243 | 244 | *请求方式:GET POST TRPC * 具体参考[client](cmd%2Fclient) 245 | 246 | 247 | **json回复:** 248 | 249 | 根对象: 250 | 251 | | 字段 | 类型 | 内容 | 备注 | 252 | |---------|-----|------|---------------------------------------------------------| 253 | | code | num | 返回值 | 0:成功
1:内部错误
2:url不支持支持的url
请参考test文件里面的地址 | 254 | | message | str | 返回信息 | 错误信息中包含id,请根据id排除,以及日志文件排除 | 255 | | data | obj | 信息本体 | 具体看proto文件
目前只返回了必要的信息 | 256 | 257 | ## 如何参与贡献 258 | 259 | 1. [fork 本项目](https://github.com/Tontonnow/ddddhmlist/fork) 260 | 2. 创建 你的特性分支 (`git checkout -b feature/xxxx`) 261 | 3. 提交 你的改动 (`git commit -am 'Add some xxx'`) 262 | 4. 推送 到分支 (`git push origin feature/xxxx`) 263 | 5. 提交一个 Pull Request 264 | 6. 等待作者合并 265 | 7. 欢迎star 266 | 8. 欢迎提交issue 267 | 9. 欢迎提交pr 268 | 269 | ### 注意事项 270 | 1. 请不要提交无意义的pr 271 | 2. 提交pr请先test 272 | 3. 新增api建议使用trpc 或者grpc 可以同时支持http和rpc 273 | 4. 新增的网站请在readme里面更新 274 | 5. 不接受解析网页,只接受api解析,建议使用TV、app端或者更新很少的接口 275 | 6. 建议协议格式为json,xml,protobuf等,尽量不需要登录 276 | 277 | ## 已知问题 278 | 279 | 1. 有些网站需要代理,请自行修改config.yaml 如hami viu 280 | 2. 优酷的mtop接口建议在国内使用,国外可能会有问题 281 | 3. iq和hami的使用TV端接口,token可能会失效(7天或更长) 282 | 283 | ## 更新日志 284 | 285 | ### 2024-04-06 286 | 287 | 1. 初版发布 288 | 289 | ## 更新计划 290 | 291 | 1. 增加语言地区选择 292 | 2. 增加国外网站(nflx,disney等) 293 | 294 | 295 | ## 提问交流 296 | 297 | * [提交issue](https://github.com/Tontonnow/ddddhmlist/issues/new) 298 | * 提问前请先阅读[提问的智慧](https://lug.ustc.edu.cn/wiki/doc/smart-questions/),无效问题会被忽略 299 | 300 | ## License 301 | 302 | [MIT](LICENSE.txt) © [Tontonnow](https://github.com/Tontonnow/ddddhmlist/blob/master/LICENSE.txt) 303 | --------------------------------------------------------------------------------