TL:DR, this thread was initially hosted at the German Forum Section (hence the following German text bloc). Bc showing up supporters (Thx @TheDewd , @GeekDude ) dealing with my request prefered using English instead I've decided to move this thread to the English Forum Section. Hope that makes sense
Hallo Forenmitglieder (welche aktuell einen YouTube-account bespaßen).
YouTube/Google hat die Option Sammlung (um channel-subscriptions/ABOs beliebig kategorisieren/ordnen zu können, aus nicht nachvollziehbaren Gründen) 2015 abgeschafft.
Aktuell lassen sich abonierte channels lediglich alphabetisch (auf)listen:
view-source:https://www.youtube.com/feed/channels bzw. YouTube (im eigenen account eingelogged) > Abos > Verwalten > sourcecode anzeigen
Der sourcecode einer solchen Liste offeriert mehrere Vorkommen des jeweiligen Links zum channel. Diese zur anschließenden Verschlagwortung zu extrahieren wäre das Ziel. Es handelt sich hier IMHO um ein in den sourcecode 'eingebettetes' JSON-Konstrukt(?), welches für den Schlüssel '"canonicalBaseUrl" den jeweiligen channel-link enthält.
Frage wäre jetzt, wie sich die channel-links am einfachsten extrahieren lassen (YouTube bietet auch eine API-Abfrage an, was die eleganteste (und bevorzugte) Lösung wäre, für mich aktuell noch 'over-the-top' ist. Beschrieben hier: https://developers.google.com/youtube/v3/docs/channels/list) ???
Neugierig verbleibend,
Das Bo
Categorize YouTube Channel List
Re: Categorize YouTube Channel List
I have over 800 subscriptions, and I've noticed that https://www.youtube.com/feed/channels does not contain all of my subscriptions in the source code. The webpage dynamically loads the subscription list as you scroll down, however the source code never contains the entire subscription list from my testing.
Use https://m.youtube.com/feed/channels?app=m instead. It forces the mobile "touch-friendly" version of the website, which shows the entire subscription list, and the source code contains the entire list too.
My thought would be to use the mobile site, get the source code, and then use AutoHotkey to parse the JSON for the data you want.
Paste the source code from the mobile site into this Gui, then click the "Parse" button.
Use https://m.youtube.com/feed/channels?app=m instead. It forces the mobile "touch-friendly" version of the website, which shows the entire subscription list, and the source code contains the entire list too.
My thought would be to use the mobile site, get the source code, and then use AutoHotkey to parse the JSON for the data you want.
Paste the source code from the mobile site into this Gui, then click the "Parse" button.
Code: Select all
#SingleInstance, Force
#Persistent
Menu, YouTubeMenu, Add, YouTube &Mobile, MenuHandler
Menu, YouTubeMenu, Add, YouTube &Desktop, MenuHandler
Menu, YouTubeMenu, Add ; Separator
Menu, YouTubeMenu, Add, YouTube M&obile (Persistent), MenuHandler
Menu, YouTubeMenu, Add, YouTube D&esktop (Persistent), MenuHandler
Gui, Margin, 10, 10
Gui, Add, Edit, x0 y0 w0 h0 0x8 ; Catch focus
Gui, Add, Link, x10 y10 w600 r1, Paste <a href="https://m.youtube.com/feed/channels?app=m">YouTube Mobile Channel List</a> source code:
Gui, Add, Edit, w600 r20 vEDT_HTML
Gui, Add, Button, w80 h24 gButtonHandler, &Parse
Gui, Add, Button, x+10 w80 h24 gButtonHandler, &YouTube
Gui, Show, AutoSize, Example
return
MenuHandler(ItemName, ItemPos, MenuName) {
Global ; Assume-global mode
If (MenuName = "YouTubeMenu") {
If (InStr(ItemName, "YouTube &Mobile")) {
Run, https://m.youtube.com/feed/channels?app=m
} Else If (InStr(ItemName, "YouTube &Desktop")) {
Run, https://www.youtube.com/?app=desktop
} Else If (InStr(ItemName, "YouTube M&obile (Persistent)")) {
Run, https://m.youtube.com/feed/channels?app=m&persist_app=1
} Else If (InStr(ItemName, "YouTube D&esktop (Persistent)")) {
Run, https://www.youtube.com/?app=desktop&persist_app=1
}
}
}
ButtonHandler() {
Global ; Assume-global mode
If (A_GuiControl = "&YouTube") {
Menu, YouTubeMenu, Show
} Else If (A_GuiControl = "&Parse") {
Gui, Submit, NoHide
Subscriptions := ""
PatChannel := "{""channelListItemRenderer"":.*?""timestampMs"":""\d+""}}"
PatChannelName := """text"":""(.*?)"""
PatChannelURL := """canonicalBaseUrl"":""(.*?)"""
Pos := 0
While (Pos := RegExMatch(EDT_HTML, PatChannel, Channel, Pos + 1)) {
RegExMatch(Channel, PatChannelName, ChannelName)
RegExMatch(Channel, PatChannelURL, ChannelURL)
Subscriptions .= ChannelName1 " - https://www.youtube.com" ChannelURL1 "`n"
}
GuiControl,, EDT_HTML, % Subscriptions
}
}
Last edited by TheDewd on 15 Nov 2020, 13:08, edited 1 time in total.
Re: Categorize YouTube Channel List
@TheDewd Nice. Unfortunately, it isn't delivering any output (probably only for me)?!
a) opened the correct list of YT subscriptions by clicking on the GUI's youtube link
b) extracted the lists source code
c) c&p it to the GUI's edit field
d) finally clicking 'Parse' and/or 'YouTube' (but nothing happens!??)
a) opened the correct list of YT subscriptions by clicking on the GUI's youtube link
b) extracted the lists source code
c) c&p it to the GUI's edit field
d) finally clicking 'Parse' and/or 'YouTube' (but nothing happens!??)
Re: Categorize YouTube Channel List
@BoBo,
My mistake! Please try again. I fixed the script.
The buttons were not functioning because I forgot to include the "&" character for "&YouTube" and "&Parse" in the ButtonHandler() function.
It should work now. Let me know if you have success this next time.
My mistake! Please try again. I fixed the script.
The buttons were not functioning because I forgot to include the "&" character for "&YouTube" and "&Parse" in the ButtonHandler() function.
It should work now. Let me know if you have success this next time.
Re: Categorize YouTube Channel List
@TheDewd Works like a charm now! Thx a lot . Once adding "about" to one of those extracted links we would be able to do a similar parsing on its resulting source ie to get the channels assigned 'tags'. What would be the parsing-component (RegEx) to extract those tags?
1st parsing output: 30X40 Design Workshop - https://www.youtube.com/user/30by40
... that if executed after adding 'about' to it https://www.youtube.com/user/30by40/about
... will provide that channel's details that contain tag-details (at its source).
"tags":["architecture","architecture tutorials","architecture students","30X40 design workshop","architecture course","residential design"]
Well, I'm not sure if those are standardized - what would it make easier to categorize/grouping a set of links, right?!
1st parsing output: 30X40 Design Workshop - https://www.youtube.com/user/30by40
... that if executed after adding 'about' to it https://www.youtube.com/user/30by40/about
... will provide that channel's details that contain tag-details (at its source).
"tags":["architecture","architecture tutorials","architecture students","30X40 design workshop","architecture course","residential design"]
Well, I'm not sure if those are standardized - what would it make easier to categorize/grouping a set of links, right?!
Re: Categorize YouTube Channel List
Here's an example to get Tags. I'll see if I can add additional parsing to the original script.
Code: Select all
#SingleInstance, Force
HttpReq := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HttpReq.Open("GET", "https://m.youtube.com/user/30by40/about", True)
HttpReq.Send()
HttpReq.WaitForResponse()
PatJSON := "{""responseContext"":.*?""trackingParams"":"".*""}"
PatTags := """tags"":\[(.*?)\]"
RegExMatch(HttpReq.ResponseText, PatJSON, MatchJSON)
RegExMatch(MatchJSON, PatTags, MatchTags)
MsgBox, % StrReplace(MatchTags1, """,""", """, """) ; Add space after comma (not necessary, but I prefer it)
Re: Categorize YouTube Channel List
Here's an attempt at including tags...
Code: Select all
#SingleInstance, Force
Menu, LoadMenu, Add, &File..., MenuHandler
Menu, LoadMenu, Add, &Clipboard, MenuHandler
Gui, Margin, 10, 10
Gui, Add, Button, xm ym w90 h24 gButtonHandler, &Load Source...
Gui, Add, Button, x+10 yp w90 h24 HWNDhGetTags gButtonHandler Disabled, &Get Tags
Gui, Add, ListView, xm y+m w600 r12 gLV vLV +LV0x4000 +LV0x10000 +NoSortHdr +AltSubmit, Channel Name|Channel URL|Channel Tags
Gui, Font, s10, Consolas
Gui, Add, Edit, xm y+10 w600 r8 +ReadOnly vEDT,
Gui, Font
Gui, Add, StatusBar,, Idle
Gui, Show, AutoSize, Example
return
MenuHandler(ItemName, ItemPos, MenuName) {
Global ; Assume-global mode
If (MenuName = "LoadMenu") {
If (InStr(ItemName, "&File...")) {
FileSelectFile, SelectedFile, 3,, &Open File..., HTML Source (*.html; *.htm; *.txt)
If (SelectedFile = "") {
return
} Else {
GetSubscriptions(SelectedFile)
}
} Else If (InStr(ItemName, "&Clipboard")) {
If !(InStr(Clipboard, "canonicalBaseUrl")) {
return
}
GetSubscriptions(Clipboard)
}
}
}
LV:
If (LV_GetNext() = 0) {
return
}
GuiControl,, EDT, % "Name: " Subscriptions[LV_GetNext()].Name "`nURL: " Subscriptions[LV_GetNext()].URL "`nTags: " Subscriptions[LV_GetNext()].Tags
return
ButtonHandler() {
Global ; Assume-global mode
If (A_GuiControl = "&Load Source...") {
Menu, LoadMenu, Show
} Else If (A_GuiControl = "&Get Tags") {
For Index, Value In Subscriptions {
SB_SetText("Retrieving Tags (" Index "/" Subscriptions.MaxIndex() ")")
Tags := GetTags(Value.URL)
LV_Modify(Index,,,, Tags)
LV_ModifyCol(3, "AutoHdr")
}
}
}
GetSubscriptions(Source) {
Global ; Assume-global mode
GuiControl, Disable, % hGetTags
LV_Delete()
If (FileExist(Source)) {
FileRead, HTML, % Source
} Else {
HTML := Clipboard
}
Subscriptions := {}
PatChannel := "{""channelListItemRenderer"":.*?""timestampMs"":""\d+""}}"
PatChannelName := """text"":""(.*?)"""
PatChannelURL := """canonicalBaseUrl"":""(.*?)"""
Pos := 0
While (Pos := RegExMatch(HTML, PatChannel, Channel, Pos + 1)) {
RegExMatch(Channel, PatChannelName, ChannelName)
RegExMatch(Channel, PatChannelURL, ChannelURL)
Subscriptions[A_Index] := {"Name": ChannelName1, "URL": "https://www.youtube.com" ChannelURL1}
}
GuiControl, -Redraw, LV
For Index, Value In Subscriptions {
SB_SetText("Parsing Subscriptions (" Index "/" Subscriptions.MaxIndex() ")")
LV_Add("", Value.Name, Value.URL)
}
Loop, % LV_GetCount("Column") {
LV_ModifyCol(A_Index, "AutoHdr")
}
GuiControl, +Redraw, LV
GuiControl, Enable, % hGetTags
SB_SetText("Idle")
}
GetTags(ChannelURL) {
Global ; Assume-global mode
HttpReq := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HttpReq.Open("GET", ChannelURL, True)
HttpReq.Send()
HttpReq.WaitForResponse()
PatJSON := "{""responseContext"":.*?""trackingParams"":"".*""}"
PatTags := """tags"":\[(.*?)\]"
RegExMatch(HttpReq.ResponseText, PatJSON, MatchJSON)
RegExMatch(MatchJSON, PatTags, MatchTags)
RetTags := StrReplace(MatchTags1, """,""", """, """)
For K, V In Subscriptions {
If (V.URL = ChannelURL) {
Subscriptions[K] := {"Name": V.Name, "URL": V.URL, "Tags": RetTags}
}
}
return RetTags
}
Re: Categorize YouTube Channel List
Looks amazing Just out of curiosity, I've had a try downloading the initial source using ComObjCreate("WinHttp.WinHttpRequest.5.1") (to get rid of that c&p/manual event,) but it delivers a different output. I assume that this is a GET vs POST request issue???
Re: Categorize YouTube Channel List
I would assume that is due to login cookies. It's probably downloading the HTML source for the Google "Login" page. I'm not advanced enough to understand how to request cookies for login using AutoHotkey, etc.
Re: Categorize YouTube Channel List
That might be of use regarding that other option - getting these details via the YouTube API ...
https://www.autohotkey.com/boards/viewtopic.php?p=263499#p263499
https://developers.google.com/youtube/v3/docs/channels/list
https://www.autohotkey.com/boards/viewtopic.php?p=263499#p263499
https://developers.google.com/youtube/v3/docs/channels/list
Re: Categorize YouTube Channel List
@BoBo,
I tried to get OAuth and API working, but it was still too difficult.
However, I had another idea. Using modified code from Neutron, by @GeekDude, I think I've developed a working solution.
It will prompt you to login, only once. Afterwards, you should be able to load your subscription list without being asked to login.
Please try the code below, and let me know if you have any issues. If it works, I'll add additional code to get the tags and other info.
I tried to get OAuth and API working, but it was still too difficult.
However, I had another idea. Using modified code from Neutron, by @GeekDude, I think I've developed a working solution.
It will prompt you to login, only once. Afterwards, you should be able to load your subscription list without being asked to login.
Please try the code below, and let me know if you have any issues. If it works, I'll add additional code to get the tags and other info.
Code: Select all
#SingleInstance, Force
#Persistent
#NoEnv
SetBatchLines, -1
Gui, +LastFound -Resize +HWNDhYouTube
Gui, +LabelYouTube
Gui, Margin, 10, 10
Gui, Add, Edit, w480 r18 +ReadOnly +HScroll vEDT
Gui, Add, Button, w80 h24 gGetSubscriptions, Get Subs
Gui, Show, AutoSize, YouTube Subscriptions Parser
neutron := new NeutronWindow()
neutron.Gui("+LabelNeutron")
neutron.wb.silent := true
neutron.Show("w640 h480 Hide")
; Allow page scrolling
SetTimer, HTMLOverflow, 250
return
GetSubscriptions() {
Global ; Assume-global mode
; Load YouTube Mobile (Persistent) website
neutron.doc.location.href := "https://m.youtube.com/feed/channels?app=m&persist_app=1"
; Wait for Subscription List loading to complete
While (!InStr(neutron.doc.location.href, "app=m")) {
; Show window if prompted for login
If (InStr(neutron.doc.location.href, "accounts.google")) {
If (DllCall("IsWindowVisible", "UInt", WinExist("ahk_id " neutron.HWnd)) = 0) {
neutron.Show()
}
}
Sleep, 250
}
; Wait for JSON to be populated
While (!RegExMatch(neutron.doc.getElementsByTagName("html")[0].innerHTML, "{""responseContext"":.*?""trackingParams"":"".*""}", Match)) {
Sleep, 250
}
; Store JSON to variable
JSON := Match
; Logout of YouTube
; neutron.doc.location.href := "https://m.youtube.com/logout?app=m&persist_app=1"
; Set URL to "about:blank"
neutron.doc.location.href := "about:blank"
; Hide window
Gui, % neutron.hWnd ": Hide"
; RegExMatch Patterns
PatChannel := "{""channelListItemRenderer"":.*?""timestampMs"":""\d+""}}"
PatChannelName := """text"":""(.*?)"""
PatChannelURL := """canonicalBaseUrl"":""(.*?)"""
Pos := 0 ; RegExMatch Position
; Find all matching patterns
While (Pos := RegExMatch(JSON, PatChannel, Channel, Pos + 1)) {
RegExMatch(Channel, PatChannelName, ChannelName)
RegExMatch(Channel, PatChannelURL, ChannelURL)
Subscriptions .= StrReplace(ChannelName1, "\u0026", "&") " - https://www.youtube.com" ChannelURL1 "`n"
}
GuiControl,, EDT, % RegExReplace(Subscriptions, "^\s+|\s+$")
}
HTMLOverflow:
Overflow := neutron.doc.getElementsByTagName("html")[0].style.overflow
If (Overflow = "") {
neutron.doc.getElementsByTagName("html")[0].style.overflow := "auto"
}
return
YouTubeClose:
NeutronClose:
ExitApp
return
Ctrl & F12:: ; Logout (Debug)
neutron.doc.location.href := "https://m.youtube.com/logout?app=m&persist_app=1"
return
;
; Neutron.ahk v1.0.0
; Copyright (c) 2020 Philip Taylor (known also as GeekDude, G33kDude)
; https://github.com/G33kDude/Neutron.ahk
;
; MIT License
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
; ==========================================================
; Neutron.ahk v1.0.0 - Modified & Reduced by TheDewd
; https://www.autohotkey.com/boards/viewtopic.php?t=76865
; https://github.com/G33kDude/Neutron.ahk
; ==========================================================
Class NeutronWindow {
Static WM_DESTROY := 0x02
, WM_SIZE := 0x05
, WM_NCLBUTTONDOWN := 0xA1
, WM_KEYDOWN := 0x100
, WM_MOUSEMOVE := 0x200
, WM_LBUTTONDOWN := 0x201
, KEY_FBE := "HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION"
, EXE_NAME := A_IsCompiled ? A_ScriptName : StrSplit(A_AhkPath, "\").Pop()
w := 800
h := 600
doc[] {
get {
return this.wb.Document
}
}
wnd[] {
get {
return this.wb.Document.parentWindow
}
}
__New() {
Static wb
this.LISTENERS := [this.WM_DESTROY, this.WM_SIZE, this.WM_KEYDOWN, this.WM_LBUTTONDOWN]
this.bound := {}
this.bound._OnMessage := this._OnMessage.Bind(this)
For i, message In this.LISTENERS {
OnMessage(message, this.bound._OnMessage)
}
Gui, New, +hWndhWnd +Resize -DPIScale
this.hWnd := hWnd
RegRead, fbe, % this.KEY_FBE, % this.EXE_NAME
RegWrite, REG_DWORD, % this.KEY_FBE, % this.EXE_NAME, 0
Gui, Add, ActiveX, vwb hWndhWB x0 y0 w800 h600, about:blank
If (fbe = "") {
RegDelete, % this.KEY_FBE, % this.EXE_NAME
} Else {
RegWrite, REG_DWORD, % this.KEY_FBE, % this.EXE_NAME, % fbe
}
this.wb := wb
this.hWB := hWB
ComObjConnect(this.wb, new this.WBEvents(this))
this.wnd.neutron := this
this.wnd.ahk := new this.Dispatch(this)
While (wb.readyState < 4) {
Sleep, 50
}
dhw := A_DetectHiddenWindows
DetectHiddenWindows, On
ControlGet, hWnd, hWnd,, Internet Explorer_Server1, % "ahk_id" this.hWnd
this.hIES := hWnd
DetectHiddenWindows, %dhw%
this.pWndProc := RegisterCallback(this._WindowProc, "", 4, &this)
this.pWndProcOld := DllCall("SetWindowLong" (A_PtrSize == 8 ? "Ptr" : ""), "Ptr", hWnd, "Int", -4, "Ptr", this.pWndProc, "Ptr")
this.wb.RegisterAsDropTarget := False
DllCall("ole32\RevokeDragDrop", "UPtr", this.hIES)
}
_OnMessage(wParam, lParam, Msg, hWnd) {
If (hWnd == this.hWnd) {
If (Msg == this.WM_SIZE) {
this.w := w := lParam<<48>>48
this.h := h := lParam<<32>>48
DllCall("MoveWindow", "UPtr", this.hWB, "Int", 0, "Int", 0, "Int", w, "Int", h, "UInt", 0)
return 0
} Else If (Msg == this.WM_DESTROY) {
For i, message In this.LISTENERS {
OnMessage(message, this.bound._OnMessage, 0)
}
this.bound := []
}
} Else If (hWnd == this.hIES) {
If (Msg == this.WM_KEYDOWN) {
If (Chr(wParam) ~= "[A-Z]" || wParam = 0x74) {
return
}
Gui +OwnDialogs
pipa := ComObjQuery(this.wb, "{00000117-0000-0000-C000-000000000046}")
VarSetCapacity(kMsg, 48), NumPut(A_GuiY, NumPut(A_GuiX, NumPut(A_EventInfo, NumPut(lParam, NumPut(wParam, NumPut(Msg, NumPut(hWnd, kMsg)))), "uint"), "int"), "int")
Loop, 2 {
r := DllCall(NumGet(NumGet(1*pipa)+5*A_PtrSize), "ptr", pipa, "ptr", &kMsg)
}
Until (wParam != 9 || this.wb.document.activeElement != "")
ObjRelease(pipa)
If (r = 0) {
return 0
}
return
}
}
}
Show(options := "") {
w := RegExMatch(options, "w\s*\K\d+", match) ? match : this.w
h := RegExMatch(options, "h\s*\K\d+", match) ? match : this.h
Gui, % this.hWnd ":Show", %options% w%w% h%h%, YouTube
}
Gui(subCommand, value1 := "", value2 := "", value3 := "") {
Gui, % this.hWnd ":" subCommand, % value1, % value2, % value3
}
Class WBEvents {
__New(parent) {
this.parent := parent
}
DocumentComplete(wb) {
wb.document.parentWindow.neutron := this.parent
wb.document.parentWindow.ahk := new this.parent.Dispatch(this.parent)
}
}
}
Last edited by TheDewd on 18 Nov 2020, 15:46, edited 2 times in total.
Re: Categorize YouTube Channel List
@TheDewd Working great!
a) I was already logged in on YT (existing open browser instance), nevertheless the script expected a login.
b) Logged off the existing browser instance, the script was still able to deliver.
Noobish guessing: both are completely isolated/separate browser instances.
I would have thought that both are using/sharing a single cookie instance?! Anyway
PS. Realized a moment ago. My current/default YT session is running within Chrome, while Neutron is working using IE/Edge by default, hence that separate cookie handling?
PPS. Let's guess that @GeekDude would transcribe the whole GUI stuff using Neutron
a) I was already logged in on YT (existing open browser instance), nevertheless the script expected a login.
b) Logged off the existing browser instance, the script was still able to deliver.
Noobish guessing: both are completely isolated/separate browser instances.
I would have thought that both are using/sharing a single cookie instance?! Anyway
PS. Realized a moment ago. My current/default YT session is running within Chrome, while Neutron is working using IE/Edge by default, hence that separate cookie handling?
PPS. Let's guess that @GeekDude would transcribe the whole GUI stuff using Neutron
! TheDewd
Re: Categorize YouTube Channel List
Actually, I believe it's using Internet Explorer (not Edge). I also think you're correct about it being isolated -- the cookies do not seem to be shared with my regular Internet Explorer session. To change the login, I included the YouTube logout link as a hotkey in the script (for debugging)
Re: Categorize YouTube Channel List
Please check GeegDudes statement regarding licensing that I've spoilered above! Thx
He's at Discord ATM, just in case you wanna chit-chat
He's at Discord ATM, just in case you wanna chit-chat
Re: Categorize YouTube Channel List
Thanks @BoBo, @GeekDude -- no harm intended. Altered the previous reply to include the license & copyright. I just removed all the lengthy text while I was testing, debugging and developing the solution to make it easier to navigate the code. Please forgive me. Too bad that people on Discord thought my code was awful. I'm embarrassed.
Re: Categorize YouTube Channel List
Code that works is not-awful by default Part of the code that I thought was not-good was copied from the AHK installer--not even code you wrote haha. I never looked closely at it before because I did not format it to put it all on 1 line, so I only noticed it in your fork here
I think it may be possible to write more efficient and reliable code than While (!RegExMatch(neutron.doc.getElementsByTagName("html")[0].innerHTML, "{""responseContext"":.*?""trackingParams"":"".*""}", Match)) { but I am not familiar with the page so I cannot say for sure. Usually checking webpage code with a regex is discouraged, and a better way can be found.
Another note is that you can use ActiveX/JS/Neutron to parse JSON pretty easily, so you can avoid using regex in that case. E.g.
I think it may be possible to write more efficient and reliable code than While (!RegExMatch(neutron.doc.getElementsByTagName("html")[0].innerHTML, "{""responseContext"":.*?""trackingParams"":"".*""}", Match)) { but I am not familiar with the page so I cannot say for sure. Usually checking webpage code with a regex is discouraged, and a better way can be found.
Another note is that you can use ActiveX/JS/Neutron to parse JSON pretty easily, so you can avoid using regex in that case. E.g.
Code: Select all
jsObj := neutron.wnd.JSON.parse(ahkStrWithJson)
MsgBox, % jsObj.key
Re: Categorize YouTube Channel List
Example of login to gmail with winhttprequest.
https://www.autohotkey.com/boards/viewtopic.php?p=365942#p365942
https://www.autohotkey.com/boards/viewtopic.php?p=365942#p365942
Re: Categorize YouTube Channel List
@malcev Thx! That might come handy if @TheDewd’s semiautomated Neutron feature isn’t the favorite option.
Re: Categorize YouTube Channel List
I decided to make this into a new project.
Posted a new script for extracting the subscription list: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=83584
Doesn't include tags, keywords, or other info yet.
Posted a new script for extracting the subscription list: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=83584
Doesn't include tags, keywords, or other info yet.
Re: Categorize YouTube Channel List
That's great news! Just in case you've a YouTube Channel in place - you could post a tiny tutorial video to spread the word.
I'd guess that's a topic that'll be of interest for many people out there. Good luck
I'd guess that's a topic that'll be of interest for many people out there. Good luck
Who is online
Users browsing this forum: CoffeeChaton, ntepa, wjt936826577 and 161 guests