From 753840764ee21f06374bf1d14afe00bd81af1c27 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:23:06 +0800 Subject: [PATCH 1/9] feat: ready field for status response --- main.go | 10 +++++++--- proto/manager.pb.go | 13 +++++++++++-- proto/manager.proto | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 351f3dd..34b44b2 100644 --- a/main.go +++ b/main.go @@ -21,8 +21,9 @@ import ( ) var ( - PROXY string - DeviceInfo string + PROXY string + DeviceInfo string + ShouldStartInstances int ) type server struct { @@ -51,6 +52,7 @@ func (s *server) Status(c context.Context, req *emptypb.Empty) (*pb.StatusReply, Status: len(Instances) != 0, Regions: regions, ClientCount: int32(len(Instances)), + Ready: len(Instances) == ShouldStartInstances, }, }, nil } @@ -500,7 +502,9 @@ func main() { Instances = make([]*WrapperInstance, 0) if _, err := os.Stat("data/storefront_ids.json"); !errors.Is(err, os.ErrNotExist) { - for _, inst := range LoadInstance() { + instancesInFile := LoadInstance() + ShouldStartInstances = len(instancesInFile) + for _, inst := range instancesInFile { go WrapperStart(inst.Id) } } diff --git a/proto/manager.pb.go b/proto/manager.pb.go index 0ef853e..964a617 100644 --- a/proto/manager.pb.go +++ b/proto/manager.pb.go @@ -132,6 +132,7 @@ type StatusData struct { Status bool `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` Regions []string `protobuf:"bytes,2,rep,name=regions,proto3" json:"regions,omitempty"` ClientCount int32 `protobuf:"varint,3,opt,name=client_count,json=clientCount,proto3" json:"client_count,omitempty"` + Ready bool `protobuf:"varint,4,opt,name=ready,proto3" json:"ready,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -187,6 +188,13 @@ func (x *StatusData) GetClientCount() int32 { return 0 } +func (x *StatusData) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + type LoginRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Data *LoginData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` @@ -1510,12 +1518,13 @@ const file_proto_manager_proto_rawDesc = "" + "\x03msg\x18\x02 \x01(\tR\x03msg\"j\n" + "\vStatusReply\x12/\n" + "\x06header\x18\x01 \x01(\v2\x17.manager.v1.ReplyHeaderR\x06header\x12*\n" + - "\x04data\x18\x02 \x01(\v2\x16.manager.v1.StatusDataR\x04data\"a\n" + + "\x04data\x18\x02 \x01(\v2\x16.manager.v1.StatusDataR\x04data\"w\n" + "\n" + "StatusData\x12\x16\n" + "\x06status\x18\x01 \x01(\bR\x06status\x12\x18\n" + "\aregions\x18\x02 \x03(\tR\aregions\x12!\n" + - "\fclient_count\x18\x03 \x01(\x05R\vclientCount\"9\n" + + "\fclient_count\x18\x03 \x01(\x05R\vclientCount\x12\x14\n" + + "\x05ready\x18\x04 \x01(\bR\x05ready\"9\n" + "\fLoginRequest\x12)\n" + "\x04data\x18\x01 \x01(\v2\x15.manager.v1.LoginDataR\x04data\"g\n" + "\tLoginData\x12\x1a\n" + diff --git a/proto/manager.proto b/proto/manager.proto index 450075b..df6ec78 100644 --- a/proto/manager.proto +++ b/proto/manager.proto @@ -33,6 +33,7 @@ message StatusData { bool status = 1; repeated string regions = 2; int32 client_count = 3; + bool ready = 4; } // ---------- Login ---------- From 00bfa3154ed2ed1b91e788dca60f8533098e5193 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:24:22 +0800 Subject: [PATCH 2/9] feat: new scheduling method --- decrypt.go | 226 +++++++++++--------------------------------- decrypt_instance.go | 137 ++++++++++++++++----------- main.go | 4 +- wrapper.go | 15 ++- 4 files changed, 146 insertions(+), 236 deletions(-) diff --git a/decrypt.go b/decrypt.go index ed1e12f..7a1ddae 100644 --- a/decrypt.go +++ b/decrypt.go @@ -1,21 +1,15 @@ package main import ( - "container/list" - "context" - "fmt" + "github.com/sirupsen/logrus" "sync" ) -var SchedulerInstance *Scheduler +var WMDispatcher *Dispatcher -type TaskGroupKey struct { - AdamId string - Key string -} - -func (t TaskGroupKey) String() string { - return fmt.Sprintf("%s|%s", t.AdamId, t.Key) +type Dispatcher struct { + Instances []*DecryptInstance + mu sync.RWMutex } type Task struct { @@ -31,186 +25,74 @@ type Result struct { Error error } -type Scheduler struct { - taskQueue *list.List - queueLock sync.Mutex - queueCond *sync.Cond - - instanceMap sync.Map // map[string]*DecryptInstance - activeKeys sync.Map // map[TaskGroupKey]bool - - ctx context.Context - cancel context.CancelFunc +func NewDispatcher() *Dispatcher { + return &Dispatcher{ + Instances: make([]*DecryptInstance, 0), + mu: sync.RWMutex{}, + } } -func NewScheduler(maxConcurrent int32) *Scheduler { - ctx, cancel := context.WithCancel(context.Background()) - s := &Scheduler{ - taskQueue: list.New(), - instanceMap: sync.Map{}, - activeKeys: sync.Map{}, - ctx: ctx, - cancel: cancel, +func (d *Dispatcher) AddInstance(inst *WrapperInstance) { + d.mu.Lock() + defer d.mu.Unlock() + decryptInstance, err := NewDecryptInstance(inst) + if err != nil { + logrus.Errorf("failed to add instance %s: %s", inst.Id, err) } - s.queueCond = sync.NewCond(&s.queueLock) - return s + d.Instances = append(d.Instances, decryptInstance) + logrus.Debugf("added instance %s", inst.Id) } -// Submit 极其简单 -func (s *Scheduler) Submit(task *Task) { - s.queueLock.Lock() - s.taskQueue.PushBack(task) - s.queueLock.Unlock() - s.queueCond.Signal() // 唤醒一个正在等待的 worker +func (d *Dispatcher) RemoveInstance(id string) { + d.mu.Lock() + defer d.mu.Unlock() + for i, inst := range d.Instances { + if inst.id == id { + d.Instances = append(d.Instances[:i], d.Instances[i+1:]...) + break + } + } } -// process 是每个 instance 的常驻协程 -func (s *Scheduler) process(instance *DecryptInstance) { - var isBroken bool - defer func() { - if isBroken { - _ = s.RemoveInstance(instance.id) // 实例损坏,移除 - } - }() - - // 外部循环:不断寻找 *新* 的 Key - for { - // 1. 等待并获取一个 *任何* 任务 - task := s.findWork(instance) - if task == nil { - return // 调度器或实例关闭 - } +func (d *Dispatcher) Submit(task *Task) { + inst := d.selectInstance(task.AdamId) + inst.Process(task) +} - groupKey := TaskGroupKey{AdamId: task.AdamId, Key: task.Key} +func (d *Dispatcher) selectInstance(adamId string) *DecryptInstance { + d.mu.RLock() + defer d.mu.RUnlock() - // 2. 尝试锁定 Key - if _, loaded := s.activeKeys.LoadOrStore(groupKey.String(), true); loaded { - // 锁定失败:此 Key 已被其他 Instance 处理 - s.requeueTask(task) // 立即放回队列 - s.queueCond.Signal() // 唤醒别人(可能包括自己) - continue // 立即寻找下一个任务 - } - - // --- 锁定成功 --- - // 我们现在独占 groupKey,必须在处理完后释放 - // 使用一个匿名函数块来管理 defer - func() { - defer s.activeKeys.Delete(groupKey.String()) // 确保锁被释放 - - // 3. 检查上下文切换 - if instance.currentKey == nil || *instance.currentKey != groupKey { - if err := instance.switchContext(groupKey); err != nil { - isBroken = true // 实例损坏 - s.requeueTask(task) // 放回任务 - task.Result <- &Result{Success: false, Error: err} - return // 退出匿名函数, 释放锁 - } - } - - // 4. 处理第一个任务 - result, err := instance.decrypt(task.Payload) - if err != nil { - isBroken = true - s.requeueTask(task) - task.Result <- &Result{Success: false, Error: err} - return // 退出匿名函数, 释放锁 - } - task.Result <- &Result{Success: true, Data: result} - - // 5. 内部“贪婪循环”:处理此 Key 的所有剩余任务 - for { - // 非阻塞地寻找下一个 *相同 Key* 的任务 - nextTask := s.findSpecificWork(groupKey) - if nextTask == nil { - // 队列中已没有此 Key 的任务,工作完成 - return // 退出匿名函数, 释放锁 - } - - // 处理下一个任务 - result, err := instance.decrypt(nextTask.Payload) - if err != nil { - isBroken = true - s.requeueTask(nextTask) - nextTask.Result <- &Result{Success: false, Error: err} - return // 实例损坏,退出匿名函数, 释放锁 - } - nextTask.Result <- &Result{Success: true, Data: result} - } - }() // 匿名函数结束 - - if isBroken { - return // 实例已损坏,退出 worker 协程 - } + if len(d.Instances) == 0 { + return nil + } - // 检查 stopCh (非阻塞) - select { - case <-instance.stopCh: - return // 实例被移除 - default: - // 继续外部循环,寻找新 Key + for _, inst := range d.Instances { + if inst.GetLastAdamId() == adamId { + logrus.Debugf("selected instance %s for adamid %s, method 1", inst.id, adamId) + return inst } } -} -// findSpecificWork (非阻塞) -// 在队列中 *搜索* 一个匹配 Key 的任务 -func (s *Scheduler) findSpecificWork(groupKey TaskGroupKey) *Task { - s.queueLock.Lock() - defer s.queueLock.Unlock() - - // 遍历 list - for e := s.taskQueue.Front(); e != nil; e = e.Next() { - task := e.Value.(*Task) - if task.AdamId == groupKey.AdamId && task.Key == groupKey.Key { - s.taskQueue.Remove(e) // 找到并移除 - return task + for _, inst := range d.Instances { + if inst.GetLastAdamId() == "" && checkAvailableOnRegion(adamId, inst.region, false) { + logrus.Debugf("selected instance %s for adamid %s, method 2", inst.id, adamId) + return inst } } - return nil // 未找到 -} -// findWork (阻塞) -// 等待并拉取队列中的 *第一个* 任务 -func (s *Scheduler) findWork(instance *DecryptInstance) *Task { - s.queueLock.Lock() - defer s.queueLock.Unlock() - - for { - // 获取队列中的第一个任务 - if e := s.taskQueue.Front(); e != nil { - task := s.taskQueue.Remove(e).(*Task) - return task + var oldestInstance *DecryptInstance + + for _, inst := range d.Instances { + if !checkAvailableOnRegion(adamId, inst.region, false) { + continue } - // 等待:队列为空 - select { - case <-s.ctx.Done(): - return nil // 调度器关闭 - case <-instance.stopCh: - return nil // 实例被移除 - default: - s.queueCond.Wait() // 等待 Submit 的信号 + if oldestInstance == nil || inst.GetLastHandleTime().Before(oldestInstance.GetLastHandleTime()) { + logrus.Debugf("selected instance %s for adamid %s, method 3", inst.id, adamId) + oldestInstance = inst } } -} -// requeueTask 将任务放回队列头部 -func (s *Scheduler) requeueTask(task *Task) { - s.queueLock.Lock() - s.taskQueue.PushFront(task) - s.queueLock.Unlock() - s.queueCond.Signal() -} - -func (s *Scheduler) Shutdown() { - s.cancel() - s.queueCond.Broadcast() // 唤醒所有等待的 worker 让他们退出 - s.instanceMap.Range(func(key, value interface{}) bool { - instance := value.(*DecryptInstance) - close(instance.stopCh) - if instance.conn != nil { - _ = instance.conn.Close() - } - return true - }) + return oldestInstance } diff --git a/decrypt_instance.go b/decrypt_instance.go index b43b3e7..f7c18e5 100644 --- a/decrypt_instance.go +++ b/decrypt_instance.go @@ -5,7 +5,7 @@ import ( "fmt" "io" "net" - "sync/atomic" + "sync" "time" ) @@ -16,12 +16,15 @@ const ( ) type DecryptInstance struct { - id string - region string - conn net.Conn - currentKey *TaskGroupKey - processing int32 - stopCh chan struct{} + id string + region string + conn net.Conn + connMu sync.Mutex + stateMu sync.RWMutex + LastAdamId string + LastKey string + LastHandleTime time.Time + Available bool } func NewDecryptInstance(inst *WrapperInstance) (*DecryptInstance, error) { @@ -30,19 +33,76 @@ func NewDecryptInstance(inst *WrapperInstance) (*DecryptInstance, error) { return nil, err } instance := &DecryptInstance{ - id: inst.Id, - region: inst.Region, - conn: conn, - stopCh: make(chan struct{}), + id: inst.Id, + region: inst.Region, + conn: conn, + LastAdamId: "", + LastKey: "", + LastHandleTime: time.Time{}, } return instance, nil } +func (d *DecryptInstance) Unavailable() { + d.conn.Close() + KillWrapper(d.id) +} + +func (d *DecryptInstance) GetLastAdamId() string { + d.stateMu.RLock() + defer d.stateMu.RUnlock() + return d.LastAdamId +} + +func (d *DecryptInstance) GetLastHandleTime() time.Time { + d.stateMu.RLock() + defer d.stateMu.RUnlock() + return d.LastHandleTime +} + +func (d *DecryptInstance) Process(task *Task) { + d.connMu.Lock() + defer d.connMu.Unlock() + + d.stateMu.Lock() + d.LastHandleTime = time.Now() + currentLastKey := d.LastKey + d.stateMu.Unlock() + + if currentLastKey != "" && currentLastKey != task.Key { + err := d.switchContext(task.AdamId, task.Key) + if err != nil { + d.Unavailable() + task.Result <- &Result{ + Success: false, + Data: task.Payload, + Error: err, + } + return + } + } + result, err := d.decrypt(task.Payload) + if err != nil { + d.Unavailable() + task.Result <- &Result{ + Success: false, + Data: task.Payload, + Error: err, + } + return + } + task.Result <- &Result{ + Success: true, + Data: result, + Error: nil, + } +} + func (d *DecryptInstance) decrypt(sample []byte) ([]byte, error) { if err := d.conn.SetDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer d.conn.SetDeadline(time.Time{}) // time.Time{} + defer d.conn.SetDeadline(time.Time{}) err := binary.Write(d.conn, binary.LittleEndian, uint32(len(sample))) if err != nil { return nil, err @@ -59,18 +119,18 @@ func (d *DecryptInstance) decrypt(sample []byte) ([]byte, error) { return de, nil } -func (d *DecryptInstance) switchContext(groupKey TaskGroupKey) error { +func (d *DecryptInstance) switchContext(adamId string, key string) error { if err := d.conn.SetDeadline(time.Now().Add(timeout)); err != nil { return err } defer d.conn.SetDeadline(time.Time{}) // time.Time{} - if d.currentKey != nil { + if d.LastKey != "" { _, err := d.conn.Write([]byte{0, 0, 0, 0}) if err != nil { return err } } - if groupKey.Key == prefetchKey { + if key == prefetchKey { _, err := d.conn.Write([]byte{byte(len(defaultId))}) if err != nil { return err @@ -80,57 +140,26 @@ func (d *DecryptInstance) switchContext(groupKey TaskGroupKey) error { return err } } else { - _, err := d.conn.Write([]byte{byte(len(groupKey.AdamId))}) + _, err := d.conn.Write([]byte{byte(len(adamId))}) if err != nil { return err } - _, err = io.WriteString(d.conn, groupKey.AdamId) + _, err = io.WriteString(d.conn, adamId) if err != nil { return err } } - _, err := d.conn.Write([]byte{byte(len(groupKey.Key))}) + _, err := d.conn.Write([]byte{byte(len(key))}) if err != nil { return err } - _, err = io.WriteString(d.conn, groupKey.Key) + _, err = io.WriteString(d.conn, key) if err != nil { return err } - d.currentKey = &groupKey - return nil -} - -func (d *DecryptInstance) IsProcessing() bool { - return atomic.LoadInt32(&d.processing) != 0 -} - -func (s *Scheduler) AddInstance(inst *WrapperInstance) error { - if _, exists := s.instanceMap.Load(inst.Id); exists { - return fmt.Errorf("instance %s already exists", inst.Id) - } - instance, err := NewDecryptInstance(inst) - if err != nil { - return err - } - s.instanceMap.Store(inst.Id, instance) - - go s.process(instance) - - return nil -} - -func (s *Scheduler) RemoveInstance(id string) error { - inst, exists := s.instanceMap.LoadAndDelete(id) - if !exists { - return fmt.Errorf("instance %s not found", id) - } - instance := inst.(*DecryptInstance) - - close(instance.stopCh) - - if instance.conn != nil { - _ = instance.conn.Close() - } + d.stateMu.Lock() + d.LastAdamId = adamId + d.LastKey = key + d.stateMu.Unlock() return nil } diff --git a/main.go b/main.go index 34b44b2..3068401 100644 --- a/main.go +++ b/main.go @@ -190,7 +190,7 @@ func (s *server) Decrypt(stream grpc.BidiStreamingServer[pb.DecryptRequest, pb.D }) continue } - SchedulerInstance.Submit(&task) + go WMDispatcher.Submit(&task) result := <-task.Result if result.Error != nil { _ = stream.Send(&pb.DecryptReply{ @@ -498,7 +498,7 @@ func main() { os.Exit(0) } - SchedulerInstance = NewScheduler(3) + WMDispatcher = NewDispatcher() Instances = make([]*WrapperInstance, 0) if _, err := os.Stat("data/storefront_ids.json"); !errors.Is(err, os.ErrNotExist) { diff --git a/wrapper.go b/wrapper.go index 85fef26..897133e 100644 --- a/wrapper.go +++ b/wrapper.go @@ -182,10 +182,7 @@ func wrapperReady(instance *WrapperInstance) { region := parseStorefrontID(string(storefrontID)) instance.Region = region InsertInstance(instance) - err = SchedulerInstance.AddInstance(instance) - if err != nil { - return - } + WMDispatcher.AddInstance(instance) instance.NoRestart = false go LoginDoneHandler(instance.Id) log.Info(fmt.Sprintf("[wrapper %s]", strings.Split(instance.Id, "-")[0]), " Wrapper ready") @@ -194,10 +191,7 @@ func wrapperReady(instance *WrapperInstance) { func wrapperDown(instance *WrapperInstance) { log.Info(fmt.Sprintf("[wrapper %s]", strings.Split(instance.Id, "-")[0]), " Wrapper Down") RemoveInstance(instance) - err := SchedulerInstance.RemoveInstance(instance.Id) - if err != nil { - return - } + WMDispatcher.RemoveInstance(instance.Id) if !instance.NoRestart { go WrapperStart(instance.Id) } else { @@ -205,6 +199,11 @@ func wrapperDown(instance *WrapperInstance) { } } +func KillWrapper(id string) error { + instance := GetInstance(id) + return instance.Cmd.Process.Kill() +} + func provide2FACode(id string, code string) { err := os.WriteFile("data/wrapper/rootfs/data/instances/"+id+"/2fa.txt", []byte(code), 0777) if err != nil { From 3b4353d726ddd1fae8f9c2b69f206fc09d973769 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:33:05 +0800 Subject: [PATCH 3/9] fix: not ready when login new account --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3068401..b6ccef7 100644 --- a/main.go +++ b/main.go @@ -52,7 +52,7 @@ func (s *server) Status(c context.Context, req *emptypb.Empty) (*pb.StatusReply, Status: len(Instances) != 0, Regions: regions, ClientCount: int32(len(Instances)), - Ready: len(Instances) == ShouldStartInstances, + Ready: len(Instances) >= ShouldStartInstances, }, }, nil } From 9a31aac3390f35be915050467eb924a9488f9590 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:33:37 +0800 Subject: [PATCH 4/9] feat: request translated and transliterated lyrics --- lyrics.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lyrics.go b/lyrics.go index a8a9e9d..151f249 100644 --- a/lyrics.go +++ b/lyrics.go @@ -22,7 +22,7 @@ func GetHttpClient() *http.Client { } func GetLyrics(adamID string, region string, language string, token string, musicToken string) (string, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/lyrics?l=%s", region, adamID, language), nil) + req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/syllable-lyrics?l[lyrics]=%s&extend=ttmlLocalizations&l[script]=en-Latn", region, adamID, language), nil) if err != nil { return "", err } @@ -53,7 +53,7 @@ func GetLyrics(adamID string, region string, language string, token string, musi } func HasLyrics(adamID string, region string, language string, token string, musicToken string) bool { - req, err := http.NewRequest("HEAD", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/lyrics?l=%s", region, adamID, language), nil) + req, err := http.NewRequest("HEAD", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/songs/%s/syllable-lyrics?l[lyrics]=%s&extend=ttmlLocalizations&l[script]=en-Latn", region, adamID, language), nil) if err != nil { return false } From eceaf299c994c0908bf2ad74d0d740d5104f2587 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:38:17 +0800 Subject: [PATCH 5/9] feat: new method for ready field --- main.go | 3 ++- wrapper.go | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index b6ccef7..c1f59c7 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( var ( PROXY string DeviceInfo string + Ready bool ShouldStartInstances int ) @@ -52,7 +53,7 @@ func (s *server) Status(c context.Context, req *emptypb.Empty) (*pb.StatusReply, Status: len(Instances) != 0, Regions: regions, ClientCount: int32(len(Instances)), - Ready: len(Instances) >= ShouldStartInstances, + Ready: Ready, }, }, nil } diff --git a/wrapper.go b/wrapper.go index 897133e..b608442 100644 --- a/wrapper.go +++ b/wrapper.go @@ -186,6 +186,9 @@ func wrapperReady(instance *WrapperInstance) { instance.NoRestart = false go LoginDoneHandler(instance.Id) log.Info(fmt.Sprintf("[wrapper %s]", strings.Split(instance.Id, "-")[0]), " Wrapper ready") + if len(Instances) == ShouldStartInstances { + Ready = true + } } func wrapperDown(instance *WrapperInstance) { From d0835a1ce49383a498bdb07103e042d5a72f8805 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Thu, 27 Nov 2025 23:49:23 +0800 Subject: [PATCH 6/9] fix: wrong field for ttml lyrics --- lyrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lyrics.go b/lyrics.go index 151f249..6aeb0e2 100644 --- a/lyrics.go +++ b/lyrics.go @@ -48,7 +48,7 @@ func GetLyrics(adamID string, region string, language string, token string, musi if respJson["errors"] != nil { return "", errors.New(fmt.Sprintf("failed to get lyrics: %s", respJson["errors"])) } - ttml := respJson["data"][0].(map[string]interface{})["attributes"].(map[string]interface{})["ttml"].(string) + ttml := respJson["data"][0].(map[string]interface{})["attributes"].(map[string]interface{})["ttmlLocalizations"].(string) return ttml, nil } From 0233f7e8f0c9c54aba234c58a4ef8f2825503c81 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Fri, 28 Nov 2025 09:34:20 +0800 Subject: [PATCH 7/9] fix: wrong switchContext logic --- decrypt_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decrypt_instance.go b/decrypt_instance.go index f7c18e5..9bb174d 100644 --- a/decrypt_instance.go +++ b/decrypt_instance.go @@ -69,7 +69,7 @@ func (d *DecryptInstance) Process(task *Task) { currentLastKey := d.LastKey d.stateMu.Unlock() - if currentLastKey != "" && currentLastKey != task.Key { + if currentLastKey == "" || currentLastKey != task.Key { err := d.switchContext(task.AdamId, task.Key) if err != nil { d.Unavailable() From ef08e848091386626853f9799b80d9b88077aa64 Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Fri, 28 Nov 2025 09:35:59 +0800 Subject: [PATCH 8/9] fix: add error handle --- decrypt_instance.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/decrypt_instance.go b/decrypt_instance.go index 9bb174d..2d388a9 100644 --- a/decrypt_instance.go +++ b/decrypt_instance.go @@ -3,6 +3,7 @@ package main import ( "encoding/binary" "fmt" + "github.com/sirupsen/logrus" "io" "net" "sync" @@ -44,8 +45,14 @@ func NewDecryptInstance(inst *WrapperInstance) (*DecryptInstance, error) { } func (d *DecryptInstance) Unavailable() { - d.conn.Close() - KillWrapper(d.id) + err := d.conn.Close() + if err != nil { + logrus.Errorf("failed to close conn of insttance %s: %s", d.id, err) + } + err = KillWrapper(d.id) + if err != nil { + logrus.Errorf("failed to kill insttance %s: %s", d.id, err) + } } func (d *DecryptInstance) GetLastAdamId() string { From 7bf7c0b8ba729b4f14b6a79bbc8cbe7632db65fc Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Fri, 28 Nov 2025 09:43:08 +0800 Subject: [PATCH 9/9] fix: remove debug code --- decrypt.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/decrypt.go b/decrypt.go index 7a1ddae..d430db4 100644 --- a/decrypt.go +++ b/decrypt.go @@ -69,14 +69,14 @@ func (d *Dispatcher) selectInstance(adamId string) *DecryptInstance { for _, inst := range d.Instances { if inst.GetLastAdamId() == adamId { - logrus.Debugf("selected instance %s for adamid %s, method 1", inst.id, adamId) + // logrus.Debugf("selected instance %s for adamid %s, method 1", inst.id, adamId) return inst } } for _, inst := range d.Instances { if inst.GetLastAdamId() == "" && checkAvailableOnRegion(adamId, inst.region, false) { - logrus.Debugf("selected instance %s for adamid %s, method 2", inst.id, adamId) + // logrus.Debugf("selected instance %s for adamid %s, method 2", inst.id, adamId) return inst } } @@ -89,7 +89,7 @@ func (d *Dispatcher) selectInstance(adamId string) *DecryptInstance { } if oldestInstance == nil || inst.GetLastHandleTime().Before(oldestInstance.GetLastHandleTime()) { - logrus.Debugf("selected instance %s for adamid %s, method 3", inst.id, adamId) + // logrus.Debugf("selected instance %s for adamid %s, method 3", inst.id, adamId) oldestInstance = inst } }