Categorize YouTube Channel List

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Categorize YouTube Channel List

13 Nov 2020, 09:55

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 :thumbup:) dealing with my request prefered using English instead I've decided to move this thread to the English Forum Section. Hope that makes sense 8-)

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 8-)
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

14 Nov 2020, 18:58

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.

:arrow: 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.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

15 Nov 2020, 13:04

@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!??)
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

15 Nov 2020, 13:19

@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. :-)
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

15 Nov 2020, 14:22

@TheDewd Works like a charm now! Thx a lot :thumbup: . 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 :arrow: 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?!
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

15 Nov 2020, 19:24

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)
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

15 Nov 2020, 21:58

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
}
Untitled.png
Untitled.png (29.47 KiB) Viewed 2102 times
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

16 Nov 2020, 12:23

Looks amazing :mrgreen: 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??? :think:
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

16 Nov 2020, 12:38

BoBo wrote:
16 Nov 2020, 12:23
...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 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.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

17 Nov 2020, 15:13

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
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

18 Nov 2020, 13:09

@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.

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.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

18 Nov 2020, 14:58

@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 :thumbup:

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 :lol:
! TheDewd
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

18 Nov 2020, 15:16

BoBo wrote:
18 Nov 2020, 14:58
PS. Realized a moment ago. My current/default YT session is running within Chrome, while Neutron is working using Edge by default, correct?
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)
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

18 Nov 2020, 15:24

Please check GeegDudes statement regarding licensing that I've spoilered above! Thx :)
He's at Discord ATM, just in case you wanna chit-chat ;)
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

18 Nov 2020, 15:35

BoBo wrote:
18 Nov 2020, 15:24
Please check GeegDudes statement regarding licensing that I've spoilered above! Thx :)
He's at Discord ATM, just in case you wanna chit-chat ;)
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. :thumbup: Too bad that people on Discord thought my code was awful. :oops: I'm embarrassed.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: Categorize YouTube Channel List

18 Nov 2020, 17:16

Code that works is not-awful by default :D 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.

Code: Select all

jsObj := neutron.wnd.JSON.parse(ahkStrWithJson)
MsgBox, % jsObj.key
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

23 Nov 2020, 01:40

@malcev Thx! That might come handy if @TheDewd’s semiautomated Neutron feature isn’t the favorite option. :thumbup:
User avatar
TheDewd
Posts: 1507
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Categorize YouTube Channel List

23 Nov 2020, 20:41

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.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Categorize YouTube Channel List

24 Nov 2020, 00:42

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 8-)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: mikeyww, ShatterCoder and 148 guests