1 (изменено: Michael, 2021-01-19 00:06:25)

Тема: Поиск на Ютубе / Хром

Доброго времени суток всем!

Необходима помощь со следующим (Win10/Chrome):

Запускаю браузер + поиск нужного запроса

Run chrome.exe http://www.youtube.com/search?q=Mafia+Definitive+Edition

Далее поиск на странице видоса с нужного канала (например: Игромания)

FoundPos := RegExMatch("Игромания", "Игромания") ;Не Понимаю, то ли я вообще сделал, ибо не работает

Далее нужно считать координаты найденного названия канала и кликнуть мышкой правее на 150 пикселей (примерно, рассчитал через ActiveWindowInfo)


Возможно есть какие то иные пути решения выше поставленных задач, может есть какие-то идеи и идеи реализации? Help, Please.


P.S.: Извиняюсь, что создал тему в Games, не знаю как её перенести просто в Общение -> AutoHotKey, администрация, сильно не пинайте

2

Re: Поиск на Ютубе / Хром

Michael пишет:

Браузер Хром

Michael пишет:

Run firefox.exe

Это надо будет подумать.

У вас тут каждый пункт на несколько тем.

Michael пишет:

какие-то идеи и идеи реализации

Попробуйте изучить это.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

3

Re: Поиск на Ютубе / Хром

serzh82saratov

Это надо будет подумать.

Поправил, просто уже перегрелся немного )

У вас тут каждый пункт на несколько тем.

Ну, всё же это работа в одном и том же приложение + действия друг от друга зависят, решил не плодить темы, запилить всё в одной

Попробуйте изучить это.

Попробую конечно, спасибо. Знать бы мне еще английский хотя бы на среднем уровне

4

Re: Поиск на Ютубе / Хром

Michael пишет:

Ну, всё же это работа в одном и том же приложение

Перед тем как самому создать приложение, надо будет ещё погреться. А тут вы выкатили ТЗ.

Michael пишет:

Попробую конечно

Тогда продвинутое RPA, они есть на русском.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

5

Re: Поиск на Ютубе / Хром

serzh82saratov
Вы правы, что-то я замахнулся сразу на велосипед, не создав колеса, поменял шапку темы, убрал много чего, оставил первостепенные задачи

6

Re: Поиск на Ютубе / Хром

Без вашего минимального кода который вы сами написали исходя из понимания Chrome.ahk, вам вряд-ли что поможет.

Michael пишет:

Не Понимаю, то ли я вообще сделал, ибо не работает

Надежды тают...

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

7

Re: Поиск на Ютубе / Хром

Возможно есть какие то иные пути решения выше поставленных задач, может есть какие-то идеи и идеи реализации? Help, Please.

Я бы пробовал сделать так:
- открываешь браузер
- заходишь на сайт
- двигаешь мышку на поиск, кликаешь, вводишь нужный запрос из пула
- потом по одному сканируешь области с видосами и распознаёшь текст при помощи "ocr ahk"
- дальше проверяешь строку на совпадение с названием нужного тебе канала
- если совпадение есть, двигаешь мышку и кликаешь на видео
- опять распознаёшь текст в области с таймингом под видео
- ожидаешь нужное время согласно продолжительности видео
- после завершения, двигаешь мышку и кликаешь на строку поиска и дальше всё повторяется с другими названиями из пула

Но это даже звучит сложнова-то, хотя вполне реально.

GD

8

Re: Поиск на Ютубе / Хром

Botsy

Я бы такое даже бы не начинал делать - движение мыши, меня смущает такая реализация. Крайне костыльно на мой взгляд.

Win10x64, AHK v1.1.37.01 (Unicode 64-bit) | AHK-Wiki | Переменные и выражения | RegEx101

9

Re: Поиск на Ютубе / Хром

__Михаил__ Сгл, но каждый реализует от своих возможностей наверно.

GD

10

Re: Поиск на Ютубе / Хром

__Михаил__
Можно вообще обойтись без движений мыши, тупо поиск слова, отступ от слова направо 150 пикселей и клик по видосу. Изучаю Chrome.ahk, но пока что для меня это темный лес..)

11

Re: Поиск на Ютубе / Хром

Botsy
Слишком много движений мыши, это пугает

Можно вообще обойтись без движений мыши, тупо поиск слова, отступ от слова направо 150 пикселей и клик по видосу. Изучаю Chrome.ahk, но пока что для меня это темный лес..)

12

Re: Поиск на Ютубе / Хром

Начал что-то делать, но успехи, такое себе...
Использую  Chrome.ahk, есть 2 момента.
1. Не понимаю, почему не работают горячие клавиши в данном скрипте, ибо выдает ошибку при добавлении "F9::"

C:\Users\Администратор\Desktop\ChromeAhk\Chrome.ahk (8) : ==> Parameters of hotkey functions must be optional.

2. По видеоуроку попытался сделать аналогичное, но с Ютубом, во текст не вводит и не кликает.
Вот, собственное, моё "творение"

#Include C:\Users\Администратор\Desktop\ChromeAhk\Chrome.ahk
#Include C:\Users\Администратор\Desktop\ChromeAhk\lib\AutoHotkey-JSON\JSON.ahk
#Include C:\Users\Администратор\Desktop\ChromeAhk\lib\WebSocket.ahk\WebSocket.ahk

#SingleInstance,Force


page:=Chrome.GetPageByTitle("YouTube") 

If !IsObject(page){
MsgBox % "Страница не найдена"
ExitApp
}

page.Evaluate("document.querySelector('#search').value ='Mafia'")
page.Evaluate("document.querySelector('#search > fieldset > button').click()")

13

Re: Поиск на Ютубе / Хром

Попробуйте так:

page.Evaluate( "document.querySelector('input#search').value ='Mafia';"
             . "var button = document.querySelector('button#search-icon-legacy');"
             . "button.focus(); button.click();" )
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14 (изменено: Michael, 2021-01-21 04:57:16)

Re: Поиск на Ютубе / Хром

teadrinker
Никакой реакции в АХК.

В консоли Хрома работает клик (только если я сам ввожу текст):

document.querySelector("#search-icon-legacy > yt-icon").click()

Поиск не работает в консоли.

UPDATE!
В консоли Хрома всё работает! В АХК - нет.
В чем может быть проблема? От Администратора тоже запускал.

15

Re: Поиск на Ютубе / Хром

Не знаю, у меня работает, только что проверил.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

16 (изменено: Michael, 2021-01-21 05:23:23)

Re: Поиск на Ютубе / Хром

teadrinker
Появляется вот такая ошибка в АХК:

C:\Users\Администратор\Desktop\ChromeAhk\lib\AutoHotkey-JSON\Jxon.ahk (174) : ==> Call to nonexistent function.
     Specifically: Jxon_Dump(k) : q . k . q ) .  ( indent ? ": " : ":" )

Может быть из-за неё? Я в Chrome.ahk и остальных скриптах вообще ничего не трогал.

Update!
Что-то в скрипте накосячилось видимо, перекачал - всё работает! Спасибо! Буду думать что дальше делать по скрипту

17

Re: Поиск на Ютубе / Хром

Используйте лучше такой вариант:

class Chrome
{
   static DebugPort := 9222
   
   /*
      Escape a string in a manner suitable for command line parameters
   */
   CliEscape(Param)
   {
      return """" RegExReplace(Param, "(\\*)""", "$1$1\""") """"
   }
   
   /*
      Finds instances of chrome in debug mode and the ports they're running
      on. If no instances are found, returns a false value. If one or more
      instances are found, returns an associative array where the keys are
      the ports, and the values are the full command line texts used to start
      the processes.
      
      One example of how this may be used would be to open chrome on a
      different port if an instance of chrome is already open on the port
      you wanted to used.
      
      ```
      ; If the wanted port is taken, use the largest taken port plus one
      DebugPort := 9222
      if (Chromes := Chrome.FindInstances()).HasKey(DebugPort)
         DebugPort := Chromes.MaxIndex() + 1
      ChromeInst := new Chrome(ProfilePath,,,, DebugPort)
      ```
      
      Another use would be to scan for running instances and attach to one
      instead of starting a new instance.
      
      ```
      if (Chromes := Chrome.FindInstances())
         ChromeInst := {"base": Chrome, "DebugPort": Chromes.MinIndex(), PID: Chromes[Chromes.MinIndex(), "PID"]}
      else
         ChromeInst := new Chrome(ProfilePath)
      ```
   */
   FindInstances()
   {
      Out := {}
      for Item in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chrome.exe'")
         if RegExMatch(Item.CommandLine, "i)chrome.exe""?\s+--remote-debugging-port=(\d+)", Match)
            Out[Match1] := {cmd: Item.CommandLine, PID: Item.ProcessId}
      return Out.MaxIndex() ? Out : False
   }
   
   /*
      ProfilePath - Path to the user profile directory to use. Will use the standard if left blank.
      URLs        - The page or array of pages for Chrome to load when it opens
      Flags       - Additional flags for chrome when launching
      ChromePath  - Path to chrome.exe, will detect from start menu when left blank
      DebugPort   - What port should Chrome's remote debugging server run on
   */
   __New(ProfilePath:="", URLs:="about:blank", Flags:="", ChromePath:="", DebugPort:="")
   {
      ; Verify ProfilePath
      if (ProfilePath != "" && !InStr(FileExist(ProfilePath), "D"))
         throw Exception("The given ProfilePath does not exist")
      this.ProfilePath := ProfilePath
      
      ; Verify ChromePath
      if (ChromePath == "")
         FileGetShortcut, %A_StartMenuCommon%\Programs\Google Chrome.lnk, ChromePath
      if (ChromePath == "")
         RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
      if !FileExist(ChromePath)
         throw Exception("Chrome could not be found")
      this.ChromePath := ChromePath
      
      ; Verify DebugPort
      if (DebugPort != "")
      {
         if DebugPort is not integer
            throw Exception("DebugPort must be a positive integer")
         else if (DebugPort <= 0)
            throw Exception("DebugPort must be a positive integer")
         this.DebugPort := DebugPort
      }
      
      ; Escape the URL(s)
      for Index, URL in IsObject(URLs) ? URLs : [URLs]
         URLString .= " " this.CliEscape(URL)
      
      Run, % this.CliEscape(ChromePath)
      . " --remote-debugging-port=" this.DebugPort
      . (ProfilePath ? " --user-data-dir=" this.CliEscape(ProfilePath) : "")
      . (Flags ? " " Flags : "")
      . URLString
      ,,, OutputVarPID
      this.PID := OutputVarPID
   }
   
   /*
      End Chrome by terminating the process.
   */
   Kill()
   {
      Process, Close, % this.PID
   }
   
   /*
      Queries chrome for a list of pages that expose a debug interface.
      In addition to standard tabs, these include pages such as extension
      configuration pages.
   */
   GetPageList()
   {
      http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
      http.open("GET", "http://127.0.0.1:" this.DebugPort "/json")
      http.send()
      return LightJson.Parse(http.responseText)
   }
   
   /*
      Returns a connection to the debug interface of a page that matches the
      provided criteria. When multiple pages match the criteria, they appear
      ordered by how recently the pages were opened.
      
      Key        - The key from the page list to search for, such as "url" or "title"
      Value      - The value to search for in the provided key
      MatchMode  - What kind of search to use, such as "exact", "contains", "startswith", or "regex"
      Index      - If multiple pages match the given criteria, which one of them to return
      fnCallback - A function to be called whenever message is received from the page
   */
   GetPageBy(Key, Value, MatchMode:="exact", Index:=1, fnCallback:="")
   {
      Count := 0
      for n, PageData in this.GetPageList()
      {
         if (((MatchMode = "exact" && PageData[Key] = Value) ; Case insensitive
            || (MatchMode = "contains" && InStr(PageData[Key], Value))
            || (MatchMode = "startswith" && InStr(PageData[Key], Value) == 1)
            || (MatchMode = "regex" && PageData[Key] ~= Value))
            && ++Count == Index)
            return new this.Page(PageData.webSocketDebuggerUrl, fnCallback)
      }
   }
   
   /*
      Shorthand for GetPageBy("url", Value, "startswith")
   */
   GetPageByURL(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
   {
      return this.GetPageBy("url", Value, MatchMode, Index, fnCallback)
   }
   
   /*
      Shorthand for GetPageBy("title", Value, "startswith")
   */
   GetPageByTitle(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
   {
      return this.GetPageBy("title", Value, MatchMode, Index, fnCallback)
   }
   
   /*
      Shorthand for GetPageBy("type", Type, "exact")
      
      The default type to search for is "page", which is the visible area of
      a normal Chrome tab.
   */
   GetPage(Index:=1, Type:="page", fnCallback:="")
   {
      return this.GetPageBy("type", Type, "exact", Index, fnCallback)
   }
   
   /*
      Connects to the debug interface of a page given its WebSocket URL.
   */
   class Page
   {
      Connected := False
      ID := 0
      Responses := []
      
      /*
         wsurl      - The desired page's WebSocket URL
         fnCallback - A function to be called whenever message is received
      */
      __New(wsurl, fnCallback:="")
      {
         this.fnCallback := fnCallback
         this.BoundKeepAlive := this.Call.Bind(this, "Browser.getVersion",, False)
         
         ; TODO: Throw exception on invalid objects
         if IsObject(wsurl)
            wsurl := wsurl.webSocketDebuggerUrl
         
         wsurl := StrReplace(wsurl, "localhost", "127.0.0.1")
         this.ws := {"base": this.WebSocket, "_Event": this.Event, "Parent": this}
         this.ws.__New(wsurl)
         
         while !this.Connected
            Sleep, 50
      }
      
      /*
         Calls the specified endpoint and provides it with the given
         parameters.
         
         DomainAndMethod - The endpoint domain and method name for the
         endpoint you would like to call. For example:
         PageInst.Call("Browser.close")
         PageInst.Call("Schema.getDomains")
         
         Params - An associative array of parameters to be provided to the
         endpoint. For example:
         PageInst.Call("Page.printToPDF", {"scale": 0.5 ; Numeric Value
         , "landscape": LightJson.true ; Boolean Value
         , "pageRanges: "1-5, 8, 11-13"}) ; String value
         PageInst.Call("Page.navigate", {"url": "https://autohotkey.com/"})
         
         WaitForResponse - Whether to block until a response is received from
         Chrome, which is necessary to receive a return value, or whether
         to continue on with the script without waiting for a response.
      */
      Call(DomainAndMethod, Params:="", WaitForResponse:=True)
      {
         if !this.Connected
            throw Exception("Not connected to tab")
         
         ; Use a temporary variable for ID in case more calls are made
         ; before we receive a response.
         ID := this.ID += 1
         this.ws.Send(LightJson.Stringify({"id": ID
         , "params": Params ? Params : {}
         , "method": DomainAndMethod}))
         
         if !WaitForResponse
            return
         
         ; Wait for the response
         this.responses[ID] := False
         while !this.responses[ID]
            Sleep, 50
         
         ; Get the response, check if it's an error
         response := this.responses.Delete(ID)
         if (response.error)
            throw Exception("Chrome indicated error in response",, LightJson.Stringify(response.error))
         
         return response.result
      }
      
      /*
         Run some JavaScript on the page. For example:
         
         PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
         PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
      */
      Evaluate(JS)
      {
         response := this.Call("Runtime.evaluate",
         ( LTrim Join
         {
            "expression": JS,
            "objectGroup": "console",
            "includeCommandLineAPI": LightJson.true,
            "silent": LightJson.false,
            "returnByValue": LightJson.false,
            "userGesture": LightJson.true,
            "awaitPromise": LightJson.false
         }
         ))
         
         if (response.exceptionDetails)
            throw Exception(response.result.description,, LightJson.Stringify(response.exceptionDetails))
         
         return response.result
      }
      
      /*
         Waits for the page's readyState to match the DesiredState.
         
         DesiredState - The state to wait for the page's ReadyState to match
         Interval     - How often it should check whether the state matches
      */
      WaitForLoad(DesiredState:="complete", Interval:=100)
      {
         while this.Evaluate("document.readyState").value != DesiredState
            Sleep, Interval
      }
      
      /*
         Internal function triggered when the script receives a message on
         the WebSocket connected to the page.
      */
      Event(EventName, Event)
      {
         ; If it was called from the WebSocket adjust the class context
         if this.Parent
            this := this.Parent
         
         ; TODO: Handle Error events
         if (EventName == "Open")
         {
            this.Connected := True
            BoundKeepAlive := this.BoundKeepAlive
            SetTimer, %BoundKeepAlive%, 15000
         }
         else if (EventName == "Message")
         {
            data := LightJson.Parse(Event.data)
            
            ; Run the callback routine
            fnCallback := this.fnCallback
            if (newData := %fnCallback%(data))
               data := newData
            
            if this.responses.HasKey(data.ID)
               this.responses[data.ID] := data
         }
         else if (EventName == "Close")
         {
            this.Disconnect()
         }
         else if (EventName == "Error")
         {
            throw Exception("Websocket Error!")
         }
      }
      
      /*
         Disconnect from the page's debug interface, allowing the instance
         to be garbage collected.
         
         This method should always be called when you are finished with a
         page or else your script will leak memory.
      */
      Disconnect()
      {
         if !this.Connected
            return
         
         this.Connected := False
         this.ws.Delete("Parent")
         this.ws.Disconnect()
         
         BoundKeepAlive := this.BoundKeepAlive
         SetTimer, %BoundKeepAlive%, Delete
         this.Delete("BoundKeepAlive")
      }
      
      class WebSocket
      {
         __New(WS_URL)
         {
            static wb
            
            ; Create an IE instance
            Gui, +hWndhOld
            Gui, New, +hWndhWnd
            this.hWnd := hWnd
            Gui, Add, ActiveX, vWB, Shell.Explorer
            Gui, %hOld%: Default
            
            ; Write an appropriate document
            WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
            . "content='IE=edge'><body></body>")
            while (WB.ReadyState < 4)
               sleep, 50
            this.document := WB.document
            
            ; Add our handlers to the JavaScript namespace
            this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
            this.document.parentWindow.ahk_event := this._Event.Bind(this)
            this.document.parentWindow.ahk_ws_url := WS_URL
            
            ; Add some JavaScript to the page to open a socket
            Script := this.document.createElement("script")
            Script.text := "ws = new WebSocket(ahk_ws_url);`n"
            . "ws.onopen = function(event){ ahk_event('Open', event); };`n"
            . "ws.onclose = function(event){ ahk_event('Close', event); };`n"
            . "ws.onerror = function(event){ ahk_event('Error', event); };`n"
            . "ws.onmessage = function(event){ ahk_event('Message', event); };"
            this.document.body.appendChild(Script)
         }
         
         ; Called by the JS in response to WS events
         _Event(EventName, Event)
         {
            this["On" EventName](Event)
         }
         
         ; Sends data through the WebSocket
         Send(Data)
         {
            this.document.parentWindow.ws.send(Data)
         }
         
         ; Closes the WebSocket connection
         Close(Code:=1000, Reason:="")
         {
            this.document.parentWindow.ws.close(Code, Reason)
         }
         
         ; Closes and deletes the WebSocket, removing
         ; references so the class can be garbage collected
         Disconnect()
         {
            if this.hWnd
            {
               this.Close()
               Gui, % this.hWnd ": Destroy"
               this.hWnd := False
            }
         }
      }
   }
}

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.eval("(" . json . ")"), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this.Stringify(v, true)

            Return isArray ? "[" str "]" : "{" str "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

18

Re: Поиск на Ютубе / Хром

teadrinker
Такс, страница открывается, поиск работает, кнопка нажимается (добавил еще загрузку ютуба):

#NoEnv
SetBatchLines, -1

class Chrome
{
   static DebugPort := 9222
   
   /*
      Escape a string in a manner suitable for command line parameters
   */
   CliEscape(Param)
   {
      return """" RegExReplace(Param, "(\\*)""", "$1$1\""") """"
   }
   
   /*
      Finds instances of chrome in debug mode and the ports they're running
      on. If no instances are found, returns a false value. If one or more
      instances are found, returns an associative array where the keys are
      the ports, and the values are the full command line texts used to start
      the processes.
      
      One example of how this may be used would be to open chrome on a
      different port if an instance of chrome is already open on the port
      you wanted to used.
      
      ```
      ; If the wanted port is taken, use the largest taken port plus one
      DebugPort := 9222
      if (Chromes := Chrome.FindInstances()).HasKey(DebugPort)
         DebugPort := Chromes.MaxIndex() + 1
      ChromeInst := new Chrome(ProfilePath,,,, DebugPort)
      ```
      
      Another use would be to scan for running instances and attach to one
      instead of starting a new instance.
      
      ```
      if (Chromes := Chrome.FindInstances())
         ChromeInst := {"base": Chrome, "DebugPort": Chromes.MinIndex(), PID: Chromes[Chromes.MinIndex(), "PID"]}
      else
         ChromeInst := new Chrome(ProfilePath)
      ```
   */
   FindInstances()
   {
      Out := {}
      for Item in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chrome.exe'")
         if RegExMatch(Item.CommandLine, "i)chrome.exe""?\s+--remote-debugging-port=(\d+)", Match)
            Out[Match1] := {cmd: Item.CommandLine, PID: Item.ProcessId}
      return Out.MaxIndex() ? Out : False
   }
   
   /*
      ProfilePath - Path to the user profile directory to use. Will use the standard if left blank.
      URLs        - The page or array of pages for Chrome to load when it opens
      Flags       - Additional flags for chrome when launching
      ChromePath  - Path to chrome.exe, will detect from start menu when left blank
      DebugPort   - What port should Chrome's remote debugging server run on
   */
   __New(ProfilePath:="", URLs:="about:blank", Flags:="", ChromePath:="", DebugPort:="")
   {
      ; Verify ProfilePath
      if (ProfilePath != "" && !InStr(FileExist(ProfilePath), "D"))
         throw Exception("The given ProfilePath does not exist")
      this.ProfilePath := ProfilePath
      
      ; Verify ChromePath
      if (ChromePath == "")
         FileGetShortcut, %A_StartMenuCommon%\Programs\Google Chrome.lnk, ChromePath
      if (ChromePath == "")
         RegRead, ChromePath, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe
      if !FileExist(ChromePath)
         throw Exception("Chrome could not be found")
      this.ChromePath := ChromePath
      
      ; Verify DebugPort
      if (DebugPort != "")
      {
         if DebugPort is not integer
            throw Exception("DebugPort must be a positive integer")
         else if (DebugPort <= 0)
            throw Exception("DebugPort must be a positive integer")
         this.DebugPort := DebugPort
      }
      
      ; Escape the URL(s)
      for Index, URL in IsObject(URLs) ? URLs : [URLs]
         URLString .= " " this.CliEscape(URL)
      
      Run, % this.CliEscape(ChromePath)
      . " --remote-debugging-port=" this.DebugPort
      . (ProfilePath ? " --user-data-dir=" this.CliEscape(ProfilePath) : "")
      . (Flags ? " " Flags : "")
      . URLString
      ,,, OutputVarPID
      this.PID := OutputVarPID
   }
   
   /*
      End Chrome by terminating the process.
   */
   Kill()
   {
      Process, Close, % this.PID
   }
   
   /*
      Queries chrome for a list of pages that expose a debug interface.
      In addition to standard tabs, these include pages such as extension
      configuration pages.
   */
   GetPageList()
   {
      http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
      http.open("GET", "http://127.0.0.1:" this.DebugPort "/json")
      http.send()
      return LightJson.Parse(http.responseText)
   }
   
   /*
      Returns a connection to the debug interface of a page that matches the
      provided criteria. When multiple pages match the criteria, they appear
      ordered by how recently the pages were opened.
      
      Key        - The key from the page list to search for, such as "url" or "title"
      Value      - The value to search for in the provided key
      MatchMode  - What kind of search to use, such as "exact", "contains", "startswith", or "regex"
      Index      - If multiple pages match the given criteria, which one of them to return
      fnCallback - A function to be called whenever message is received from the page
   */
   GetPageBy(Key, Value, MatchMode:="exact", Index:=1, fnCallback:="")
   {
      Count := 0
      for n, PageData in this.GetPageList()
      {
         if (((MatchMode = "exact" && PageData[Key] = Value) ; Case insensitive
            || (MatchMode = "contains" && InStr(PageData[Key], Value))
            || (MatchMode = "startswith" && InStr(PageData[Key], Value) == 1)
            || (MatchMode = "regex" && PageData[Key] ~= Value))
            && ++Count == Index)
            return new this.Page(PageData.webSocketDebuggerUrl, fnCallback)
      }
   }
   
   /*
      Shorthand for GetPageBy("url", Value, "startswith")
   */
   GetPageByURL(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
   {
      return this.GetPageBy("url", Value, MatchMode, Index, fnCallback)
   }
   
   /*
      Shorthand for GetPageBy("title", Value, "startswith")
   */
   GetPageByTitle(Value, MatchMode:="startswith", Index:=1, fnCallback:="")
   {
      return this.GetPageBy("title", Value, MatchMode, Index, fnCallback)
   }
   
   /*
      Shorthand for GetPageBy("type", Type, "exact")
      
      The default type to search for is "page", which is the visible area of
      a normal Chrome tab.
   */
   GetPage(Index:=1, Type:="page", fnCallback:="")
   {
      return this.GetPageBy("type", Type, "exact", Index, fnCallback)
   }
   
   /*
      Connects to the debug interface of a page given its WebSocket URL.
   */
   class Page
   {
      Connected := False
      ID := 0
      Responses := []
      
      /*
         wsurl      - The desired page's WebSocket URL
         fnCallback - A function to be called whenever message is received
      */
      __New(wsurl, fnCallback:="")
      {
         this.fnCallback := fnCallback
         this.BoundKeepAlive := this.Call.Bind(this, "Browser.getVersion",, False)
         
         ; TODO: Throw exception on invalid objects
         if IsObject(wsurl)
            wsurl := wsurl.webSocketDebuggerUrl
         
         wsurl := StrReplace(wsurl, "localhost", "127.0.0.1")
         this.ws := {"base": this.WebSocket, "_Event": this.Event, "Parent": this}
         this.ws.__New(wsurl)
         
         while !this.Connected
            Sleep, 50
      }
      
      /*
         Calls the specified endpoint and provides it with the given
         parameters.
         
         DomainAndMethod - The endpoint domain and method name for the
         endpoint you would like to call. For example:
         PageInst.Call("Browser.close")
         PageInst.Call("Schema.getDomains")
         
         Params - An associative array of parameters to be provided to the
         endpoint. For example:
         PageInst.Call("Page.printToPDF", {"scale": 0.5 ; Numeric Value
         , "landscape": LightJson.true ; Boolean Value
         , "pageRanges: "1-5, 8, 11-13"}) ; String value
         PageInst.Call("Page.navigate", {"url": "https://autohotkey.com/"})
         
         WaitForResponse - Whether to block until a response is received from
         Chrome, which is necessary to receive a return value, or whether
         to continue on with the script without waiting for a response.
      */
      Call(DomainAndMethod, Params:="", WaitForResponse:=True)
      {
         if !this.Connected
            throw Exception("Not connected to tab")
         
         ; Use a temporary variable for ID in case more calls are made
         ; before we receive a response.
         ID := this.ID += 1
         this.ws.Send(LightJson.Stringify({"id": ID
         , "params": Params ? Params : {}
         , "method": DomainAndMethod}))
         
         if !WaitForResponse
            return
         
         ; Wait for the response
         this.responses[ID] := False
         while !this.responses[ID]
            Sleep, 50
         
         ; Get the response, check if it's an error
         response := this.responses.Delete(ID)
         if (response.error)
            throw Exception("Chrome indicated error in response",, LightJson.Stringify(response.error))
         
         return response.result
      }
      
      /*
         Run some JavaScript on the page. For example:
         
         PageInst.Evaluate("alert(""I can't believe it's not IE!"");")
         PageInst.Evaluate("document.getElementsByTagName('button')[0].click();")
      */
      Evaluate(JS)
      {
         response := this.Call("Runtime.evaluate",
         ( LTrim Join
         {
            "expression": JS,
            "objectGroup": "console",
            "includeCommandLineAPI": LightJson.true,
            "silent": LightJson.false,
            "returnByValue": LightJson.false,
            "userGesture": LightJson.true,
            "awaitPromise": LightJson.false
         }
         ))
         
         if (response.exceptionDetails)
            throw Exception(response.result.description,, LightJson.Stringify(response.exceptionDetails))
         
         return response.result
      }
      
      /*
         Waits for the page's readyState to match the DesiredState.
         
         DesiredState - The state to wait for the page's ReadyState to match
         Interval     - How often it should check whether the state matches
      */
      WaitForLoad(DesiredState:="complete", Interval:=100)
      {
         while this.Evaluate("document.readyState").value != DesiredState
            Sleep, Interval
      }
      
      /*
         Internal function triggered when the script receives a message on
         the WebSocket connected to the page.
      */
      Event(EventName, Event)
      {
         ; If it was called from the WebSocket adjust the class context
         if this.Parent
            this := this.Parent
         
         ; TODO: Handle Error events
         if (EventName == "Open")
         {
            this.Connected := True
            BoundKeepAlive := this.BoundKeepAlive
            SetTimer, %BoundKeepAlive%, 15000
         }
         else if (EventName == "Message")
         {
            data := LightJson.Parse(Event.data)
            
            ; Run the callback routine
            fnCallback := this.fnCallback
            if (newData := %fnCallback%(data))
               data := newData
            
            if this.responses.HasKey(data.ID)
               this.responses[data.ID] := data
         }
         else if (EventName == "Close")
         {
            this.Disconnect()
         }
         else if (EventName == "Error")
         {
            throw Exception("Websocket Error!")
         }
      }
      
      /*
         Disconnect from the page's debug interface, allowing the instance
         to be garbage collected.
         
         This method should always be called when you are finished with a
         page or else your script will leak memory.
      */
      Disconnect()
      {
         if !this.Connected
            return
         
         this.Connected := False
         this.ws.Delete("Parent")
         this.ws.Disconnect()
         
         BoundKeepAlive := this.BoundKeepAlive
         SetTimer, %BoundKeepAlive%, Delete
         this.Delete("BoundKeepAlive")
      }
      
      class WebSocket
      {
         __New(WS_URL)
         {
            static wb
            
            ; Create an IE instance
            Gui, +hWndhOld
            Gui, New, +hWndhWnd
            this.hWnd := hWnd
            Gui, Add, ActiveX, vWB, Shell.Explorer
            Gui, %hOld%: Default
            
            ; Write an appropriate document
            WB.Navigate("about:<!DOCTYPE html><meta http-equiv='X-UA-Compatible'"
            . "content='IE=edge'><body></body>")
            while (WB.ReadyState < 4)
               sleep, 50
            this.document := WB.document
            
            ; Add our handlers to the JavaScript namespace
            this.document.parentWindow.ahk_savews := this._SaveWS.Bind(this)
            this.document.parentWindow.ahk_event := this._Event.Bind(this)
            this.document.parentWindow.ahk_ws_url := WS_URL
            
            ; Add some JavaScript to the page to open a socket
            Script := this.document.createElement("script")
            Script.text := "ws = new WebSocket(ahk_ws_url);`n"
            . "ws.onopen = function(event){ ahk_event('Open', event); };`n"
            . "ws.onclose = function(event){ ahk_event('Close', event); };`n"
            . "ws.onerror = function(event){ ahk_event('Error', event); };`n"
            . "ws.onmessage = function(event){ ahk_event('Message', event); };"
            this.document.body.appendChild(Script)
         }
         
         ; Called by the JS in response to WS events
         _Event(EventName, Event)
         {
            this["On" EventName](Event)
         }
         
         ; Sends data through the WebSocket
         Send(Data)
         {
            this.document.parentWindow.ws.send(Data)
         }
         
         ; Closes the WebSocket connection
         Close(Code:=1000, Reason:="")
         {
            this.document.parentWindow.ws.close(Code, Reason)
         }
         
         ; Closes and deletes the WebSocket, removing
         ; references so the class can be garbage collected
         Disconnect()
         {
            if this.hWnd
            {
               this.Close()
               Gui, % this.hWnd ": Destroy"
               this.hWnd := False
            }
         }
      }
   }
}

class LightJson
{
   static JS := LightJson.GetJS(), true := {}, false := {}, null := {}
   
   Parse(json, _rec := false) {
      if !_rec
         obj := this.Parse(this.JS.eval("(" . json . ")"), true)
      else if !IsObject(json)
         obj := json
      else if this.JS.Object.prototype.toString.call(json) == "[object Array]" {
         obj := []
         Loop % json.length
            obj.Push( this.Parse(json[A_Index - 1], true) )
      }
      else {
         obj := {}
         keys := this.JS.Object.keys(json)
         Loop % keys.length {
            k := keys[A_Index - 1]
            obj[k] := this.Parse(json[k], true)
         }
      }
      Return obj
   }
   
   Stringify(obj, indent := "") {
      if indent|1 {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v

         if IsObject( obj ) {
            isArray := true
            for key in obj {
               if IsObject(key)
                  throw Exception("Invalid key")
               if !( key = A_Index || isArray := false )
                  break
            }
            for k, v in obj
               str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this.Stringify(v, true)

            Return isArray ? "[" str "]" : "{" str "}"
         }
         else if !(obj*1 = "" || RegExMatch(obj, "^-?0|\s"))
            Return obj
         
         for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(8), "\b"]]
            obj := StrReplace( obj, v[1], v[2] )

         Return """" obj """"
      }
      sObj := this.Stringify(obj, true)
      Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
   }
   
   GetJS() {
      static Doc, JS
      if !Doc {
         Doc := ComObjCreate("htmlfile")
         Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         JS := Doc.parentWindow
         ( Doc.documentMode < 9 && JS.execScript() )
      }
      Return JS
   }
}

F10::

page:=Chrome.GetPageByTitle("Новая вкладка") 

If !IsObject(page){
MsgBox % "Страница не найдена"
ExitApp
}

page.Call("Page.navigate", {"url": "https://www.youtube.com/"})
page.WaitForLoad()

page:=Chrome.GetPageByTitle("YouTube") 

If !IsObject(page){
MsgBox % "Страница не найдена"
ExitApp
}

page.Evaluate( "document.querySelector('input#search').value ='Mafia';"
             . "var button = document.querySelector('button#search-icon-legacy');"
             . "button.focus(); button.click();" )

Подскажите, мне необходимо теперь сделать после выполнение вышеописанного кода поиск по странице, нужно найти слово (название канала) и кликнуть на 150 пикселей правее от него (видео с этого канала). Не могу загуглить ничего похожего.

19

Re: Поиск на Ютубе / Хром

А зачем кликать по пикселям? Ищите нужные элементы так же с помощью джаваскрипта и кликайте программно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

20 (изменено: teadrinker, 2021-01-21 06:27:33)

Re: Поиск на Ютубе / Хром

Кстати, изначально ни к чему искать строку поиска и кликать, просто исрользуйте url для поиска по YouTube: https://www.youtube.com/results?search_query=Mafia
Да, и объект страницы два раза не нужно получать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: Поиск на Ютубе / Хром

teadrinker

Что-то я окончательно запутался, уже 6 утра
Вот код нужного элемента из хрома:

<a class="yt-simple-endpoint style-scope yt-formatted-string" spellcheck="false" href="/user/StopGameRu" dir="auto">StopGame.Ru</a>

Где StopGameRu - адрес канала, Stopgame.ru - название канала.
Мне нужно сделать поиск по этому слову.
И чтобы в итоге кликнул он не на канал, а на видос, тот что слева от него.
А видос выдает в коде вот это:

<div id="mouseover-overlay" class="style-scope ytd-thumbnail"></div>

В JS Path вообще выдает такое:

document.querySelector("#text > a")

Как это всё дело сопоставить - не понимаю, сижу и вдупляю в монитор уже который час

22

Re: Поиск на Ютубе / Хром

На странице с найденным такой джаваскрипт:

(() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

23

Re: Поиск на Ютубе / Хром

teadrinker
В консоли всё работает.

В АХК, я так понимаю, надо использовать JSON?
Нашел тему http://forum.script-coding.com/viewtopic.php?id=14437, пытаюсь понять что там, и к чему

24

Re: Поиск на Ютубе / Хром

Я не понимаю...
Вот Ваш пример использования:

jsonStr := JSON.GetFromUrl("https://api.coindesk.com/v1/bpi/currentprice.json")

if IsObject(jsonStr)  {
   MsgBox, % jsonStr[1]
   Return
}
if (jsonStr = "")
   Return

MsgBox, 64, JSON, % jsonStr

obj := JSON.Parse(jsonStr)
MsgBox, 64, Bitcoin Price
      , % "upd: " . obj.time.updated . "`n`n"
        . "USD: " . obj.bpi.USD.rate . "`n"
        . "GBP: " . obj.bpi.GBP.rate . "`n"
        . "EUR: " . obj.bpi.EUR.rate
        
MsgBox, 64, JSON From Obj, % jsonStr := JSON.Stringify(obj)

Тут, JS извлекается с URL, как я понимаю, а у меня уже готовый, то что выше писали.
Я засунул JS в ваш скрипт (я вижу и понимаю, что это откровенный бред, у меня уже мозги кипят), ну и собственно ничего не работает

jsonStr := (() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();
if IsObject(jsonStr)  {
   MsgBox, % jsonStr[1]
   Return
}
if (jsonStr = "")
   Return

MsgBox, 64, JSON, % jsonStr

obj := JSON.Parse(jsonStr)
MsgBox, 64, Bitcoin Price
      , % "upd: " . obj.time.updated . "`n`n"
        . "USD: " . obj.bpi.USD.rate . "`n"
        . "GBP: " . obj.bpi.GBP.rate . "`n"
        . "EUR: " . obj.bpi.EUR.rate
        
MsgBox, 64, JSON From Obj, % jsonStr := JSON.Stringify(obj)

Я пересмотрел трижды видосы этого человека, такие как - этот и этот, где он делает нечто подобное.

Также нашел код, который делает вроде как нечто аналогичное:

/****************************************************************************************
    Function: BuildJson(obj) 
        Builds a JSON string from an AutoHotkey object
 
    Parameters:
        obj - An AutoHotkey array or object, which can include nested objects.
 
    Remarks:
        Originally Obj2Str() by Coco,
        http://www.autohotkey.com/board/topic/93300-what-format-to-store-settings-in/page-2#entry588373
        
        Modified to use double quotes instead of single quotes and to leave numeric values
        unquoted.
 
    Returns:
        The JSON string
*/
BuildJson(obj) 
{
    str := "" , array := true
    for k in obj {
        if (k == A_Index)
            continue
        array := false
        break
    }
    for a, b in obj
        str .= (array ? "" : """" a """: ") . (IsObject(b) ? BuildJson(b) : IsNumber(b) ? b : """" b """") . ", "   
    str := RTrim(str, " ,")
    return (array ? "[" str "]" : "{" str "}")
}
 
/****************************************************************************************
    Function: ParseJson(jsonStr)
        Converts a JSON string into an AutoHotkey object
 
    Parameters:
        jsonstr - the JSON string to convert
 
    Remarks:
        Originally by Getfree,
        http://www.autohotkey.com/board/topic/93300-what-format-to-store-settings-in/#entry588268
 
    Returns:
        The AutoHotkey object.
*/
ParseJson(jsonStr)
{
    SC := ComObjCreate("ScriptControl") 
    SC.Language := "JScript"
    ComObjError(false)
    jsCode =
    (
    function arrangeForAhkTraversing(obj){
        if(obj instanceof Array){
            for(var i=0 ; i<obj.length ; ++i)
                obj[i] = arrangeForAhkTraversing(obj[i]) ;
            return ['array',obj] ;
        }else if(obj instanceof Object){
            var keys = [], values = [] ;
            for(var key in obj){
                keys.push(key) ;
                values.push(arrangeForAhkTraversing(obj[key])) ;
            }
            return ['object',[keys,values]] ;
        }else
            return [typeof obj,obj] ;
    }
    )
    SC.ExecuteStatement(jsCode "; obj=" jsonStr)
    return convertJScriptObjToAhks( SC.Eval("arrangeForAhkTraversing(obj)") )
}
 
/*!
    Function: convertJScriptObjToAhks(jsObj)
        Used by ParseJson()
*/
convertJScriptObjToAhks(jsObj)
{
    if(jsObj[0]="object"){
        obj := {}, keys := jsObj[1][0], values := jsObj[1][1]
        loop % keys.length
            obj[keys[A_INDEX-1]] := convertJScriptObjToAhks( values[A_INDEX-1] )
        return obj
    }else if(jsObj[0]="array"){
        array := []
        loop % jsObj[1].length
            array.insert(convertJScriptObjToAhks( jsObj[1][A_INDEX-1] ))
        return array
    }else
        return jsObj[1]
}
 
/*!
    Function: IsNumber(Num)
        Checks if Num is a number.
 
    Returns:
        True if Num is a number, false if not
*/
IsNumber(Num)
{
    if Num is number
        return true
    else
        return false
}

В "JSCode=" вставил код

ParseJson(jsonStr)
{
    SC := ComObjCreate("ScriptControl") 
    SC.Language := "JScript"
    ComObjError(false)
   jsCode =
    (() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();
SC.ExecuteStatement(jsCode "; obj=" jsonStr)
    return convertJScriptObjToAhks( SC.Eval("arrangeForAhkTraversing(obj)") )
}

Но он тоже отказывается работать, везде ошибки в строке "const coll = document.querySelectorAll('div#channel-info');"
Ошибка: This line does not contain a recognized action.

25

Re: Поиск на Ютубе / Хром

Michael пишет:

В АХК, я так понимаю, надо использовать JSON?

JSON тут никаким боком (не говоря уж о том, что джаваскрипт и JSON не одно и то же). Вам нужно просто выполнить скрипт на странице, так же, как вы уже это делали, когда определяли элемент строки поиска.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

26

Re: Поиск на Ютубе / Хром

teadrinker
Извиняюсь, когда много новой непонятной информации поступает в мой мозг, со мной случается то, что было в пред. моем посте
Вот так:

page.Evaluate(() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})(); 

И вот так:

page.Evaluate((() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})(); )

А также, вот так:

page.Evaluate("(() => {"
   "const coll = document.querySelectorAll('div#channel-info');"
   "let elem;"
   "for (let item of coll) {"
      "if ( item.innerText == 'StopGame.Ru' && (elem = item) )"
         "break;"
   "};"
   "if (elem)"
      "elem.parentNode.parentNode.querySelector('a#thumbnail').click();"
   "else"
      "alert('Не найдено!');"
"})();" )

Ошибка в строке "const coll = document.querySelectorAll('div#channel-info');" [This line does not contain a recognized action.]
Понимаю, что делаю методом тыка, и, исходя из предыдущего удачного примера выполнения скрипта.

27

Re: Поиск на Ютубе / Хром

Ищите примеры, как поместить многострочный текст в переменную.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

28

Re: Поиск на Ютубе / Хром

teadrinker
После нескольких часов поиска сделал так (примера, как такового не нашел, на англоязычных тоже...):

page.Evaluate(() => "{const coll = document.querySelectorAll('div#channel-info');"
   ,"let elem;"
   ,"for (let item of coll) {"
      ,"if ( item.innerText == 'StopGame.Ru' && (elem = item) )"
         ,"break;"
   ,"};"
   ,"if (elem)"
      ,"elem.parentNode.parentNode.querySelector('a#thumbnail').click();"
   ,"else"
      ,"alert('Не найдено!');"
,"})();" )

Ошибок при компиляции нету, что уже радует, но сам скрипт не работает, не ищет и не кликает на видос

29 (изменено: teadrinker, 2021-01-22 01:26:09)

Re: Поиск на Ютубе / Хром

Опять мимо. Джаваскрипт, который я привёл

(() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();

с точки зрения AHK просто текст, начиная с ( и заканчивая ;. Вам нужно целиком поместить его в переменную, например js и уже эту переменную передать в выражение: page.Evaluate(js). Вот и найдите способ поместить код в переменную, так, чтобы его можно было показать в MsgBox: MsgBox % js

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30 (изменено: Michael, 2021-01-22 02:05:57)

Re: Поиск на Ютубе / Хром

teadrinker
Такс, ну в MsgBox текст выводит (скрин 1), а вот при page.Evaluate(js) выдает ошибку (скрин 2):

js = (() => {, const coll = document.querySelectorAll('div#channel-info');, let elem;, for (let item of coll) {, if ( item.innerText == 'StopGame.Ru' && (elem = item) ), break;, };, if (elem), elem.parentNode.parentNode.querySelector('a#thumbnail').click();, else, alert('Не найдено!');, })();

page.Evaluate(js)
;MsgBox % js

UPDATE! Сам понял, где косяк в переменной. Переделал.
Теперь отображается в MsgBox точь в точь как вы прислали, с разделением на строки и без лишних символов.

js := "(() => {`n   const coll = document.querySelectorAll('div#channel-info');`n   let elem;`n   for (let item of coll) {`n      if ( item.innerText == 'StopGame.Ru' && (elem = item) )`n         break;`n   };`n   if (elem)`n      elem.parentNode.parentNode.querySelector('a#thumbnail').click();`n   else`n      alert('Не найдено!');`n})();"

Теперь нарисовалась проблема в самом хроме, который говорит, что окошко не найдено из [alert('Не найдено!')], что говорит о том, что скрипт хотя бы что-то искал, но не нашел. Не пойму почему.

31

Re: Поиск на Ютубе / Хром

Вот так лучше:

js =
(
(() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();
)
MsgBox, % js
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

32

Re: Поиск на Ютубе / Хром

teadrinker
Добавил Sleep и всё получилось, ищет нужный канал и кликает по нему! Спасибо за помощь!
Вот текущий скрипт:

F10::

page:=Chrome.GetPageByTitle("Новая вкладка") 
page.Call("Page.navigate", {"url": "https://www.youtube.com/"})
page.WaitForLoad()
page:=Chrome.GetPageByTitle("YouTube")

;вбиваем запрос в поисковик Ютуба
page.Evaluate( "document.querySelector('input#search').value ='Mafia';"
             . "var button = document.querySelector('button#search-icon-legacy');"
             . "button.focus(); button.click();" )
sleep (3000) ; ждем прогрузку         
             

;поиск нужного канала по имени + клик по видосу
js =
(
(() => {
   const coll = document.querySelectorAll('div#channel-info');
   let elem;
   for (let item of coll) {
      if ( item.innerText == 'StopGame.Ru' && (elem = item) )
         break;
   };
   if (elem)
      elem.parentNode.parentNode.querySelector('a#thumbnail').click();
   else
      alert('Не найдено!');
})();
)

page.Evaluate(js)

sleep (150000) ; ждем пока пройдет реклама

Буду пытаться делать дальше задуманное.
Следующий этап: получение продолжительности ролика и запись этого времени в Sleep.

33

Re: Поиск на Ютубе / Хром

Пытаюсь сделать поиск длительности видоса.
Вот что пока удалось выяснить:
После перехода на конкретное видео в коде уже не найти длительность видео (либо оно хорошо зашифровано / лежит в ФлешПлеере)
Следовательно узнавать длительность надо со страницы поиска, там она есть, в текстовом формате на каждый видос.
Код элемента показывает на [document.querySelector('div#overlays')]

document.querySelector("#overlays > ytd-thumbnail-overlay-time-status-renderer > span")
<span class="style-scope ytd-thumbnail-overlay-time-status-renderer" aria-label="22 минуты 15 секунд">
  22:15
</span>

Span, и в нем Aria-Label, значение которого мне как раз и нужно. Как мне его оттуда получить и записать полученное в [Sleep]? Подскажите, люди добрые.

34 (изменено: teadrinker, 2021-01-22 04:15:13)

Re: Поиск на Ютубе / Хром

Michael пишет:

Добавил Sleep и всё получилось

Есть page.WaitForLoad().

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

35

Re: Поиск на Ютубе / Хром

teadrinker
Это да, так более правильно будет в моем случае.
Не посоветуете в каком направлении идти, чтобы узнать длительность видео с выбранного канала? Я много уже чего посмотрел в плане уроков и прочего, но я не понимаю что мне конкретно искать нужно...

36

Re: Поиск на Ютубе / Хром

document.querySelector('video').duration
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

37

Re: Поиск на Ютубе / Хром

teadrinker
Как я понимаю, должно что-то около того получаться, исходя из отдаленно похожих примеров из Гугла.
Только вот как из константы вывести result, что-то не догоняю. В консоли браузере [document.querySelector('video').duration] отображает на открытом видосе его продолжительность в секундах, тут всё работает.

vd =
(
const result = document.querySelector('video').duration";
)

MsgBox % result
;page.Evaluate(vd)

38

Re: Поиск на Ютубе / Хром

По идее должно так работать, не проверял:

MsgBox % page.Evaluate("document.querySelector('video').duration").value
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

39

Re: Поиск на Ютубе / Хром

teadrinker
Выводит время в секундах в формате 999.111111, где 999 - секунды, 111111 - миллисекунды.

MsgBox % page.Evaluate("document.querySelector('video').duration").value

Мне, соответственно, нужно округлить до секунд, убрав миллисекунды (еще умножить на 60, чтобы получить миллисекунды в итоге, но это чуть позже).

Пробую так - пустой MsgBox.

MsgBox % page.Evaluate("document.querySelector('video').duration").value(Ceil)

Пробую вот так - пустой MsgBox.

page.Evaluate("document.querySelector('video').duration").value
var = 
(
page.Evaluate("document.querySelector('video').duration").value
)

var := Ceil(var2)
MsgBox % var2

Что я опять делаю не так?

40

Re: Поиск на Ютубе / Хром

Наверно слишком рано взяли такую сложную тему, вам бы опыта поднабраться годик-другой.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

41

Re: Поиск на Ютубе / Хром

teadrinker
Возможно, но выбор у меня не велик. Мне же требуется конкретная задача. Я бы с радостью поднабрался опыта, но через годик два мне этот скрипт маловероятно понадобиться, поэтому и пришёл на данный форум, чтобы умные люди подсказали / показали / объяснили нубу что и как

42

Re: Поиск на Ютубе / Хром

Ну, одно дело какие-то моменты посказать, другое — полностью написать. Для таких целей есть Коммерческий раздел.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

43

Re: Поиск на Ютубе / Хром

teadrinker
Можно хотя бы теорию изучить по моему вопросу? Хотя бы что искать, почитать, посмотреть

44

Re: Поиск на Ютубе / Хром

А по какому именно? У вас каждый раз вопросы разные.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

45

Re: Поиск на Ютубе / Хром

teadrinker
Вывести результат не в MsgBox, а в переменную, для начала.
Далее, как я понимаю, уже работать с этой переменной и делать математические действия.

46

Re: Поиск на Ютубе / Хром

https://www.autohotkey.com/docs/Variables.htm

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

47

Re: Поиск на Ютубе / Хром

teadrinker
Данный код помещает нужное значение в переменную. Уже прогресс.

page.Evaluate("document.querySelector('video').duration").value
save:=page.Evaluate("document.querySelector('video').duration").value
MsgBox % save

Теперь нужно округлить значение переменной Save до целых, и, соответственно, умножить на 1000 (перевод секунд в миллисекунды).
Вопрос. Делать это прямо в строке "save:=..." или же новую строку как-то выводить нужно?

48

Re: Поиск на Ютубе / Хром

Нет, так дело не пойдёт. Вы по каждому следующему шагу будете задавать вопросы? Тогда у нас есть такое правило: одна тема — один вопрос. По-моему тема, заявленная в заголовке себя уже исчерпала. Так что создавайте новую, формулируйте заголовок, задавайте внятный вопрос, и если кому-то он покажется интересным, ответят. Ну а вообще-то, как я уже говорил, данная задача для вас на текущем уровне знаний абсолютно неподъёмна, тот самый случай, когда нужно обращаться в Коммерческий раздел.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

49

Re: Поиск на Ютубе / Хром

teadrinker
Насчет темы понял, а насчет коммерции - в моем случае - нет, ибо готовый полный код сразу мне не нужен, я хочу именно попытаться разобраться и частично сделать что-то сам.

50

Re: Поиск на Ютубе / Хром

Начали со слишком сложной задачи, в этом для вас не будет никакого смысла. Чтобы понять сложные вещи, надо хорошо понимать простые.
А разбираться самостоятельно никто же не мешает, открыли справку, и вперёд, лично я так делал.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder