├── .gitignore
├── CITATION.cff
├── README.md
├── exercises
├── A2_The_Youtube_API_exercises_question.html
├── A3_tuber_exercises_question.html
├── A4_Preprocessing_and_cleaning_data_exercises_question.html
├── B1_Basic_text_analysis_exercises_question.html
└── B2_Sentiment_analysis_of_user_comments_exercises_question.html
├── scripts
├── Authentication.R
├── CamelCase.R
├── ExtractEmoji.R
├── ReplaceEmoji.R
├── YouTubeAPIAccess.R
├── YouTubeSubtitles.R
├── emoji_mapping_function.R
├── install_packages.R
├── video_details_to_df.R
└── yt_parse.R
├── slides
├── A0_YouTube_API_Setup
│ ├── A0_YouTubeAPISetup.Rmd
│ ├── A0_YouTubeAPISetup.html
│ ├── A0_YouTubeAPISetup.pdf
│ ├── Images
│ │ ├── 0.png
│ │ ├── 1.png
│ │ ├── 10.png
│ │ ├── 11.png
│ │ ├── 12.png
│ │ ├── 13.png
│ │ ├── 14.png
│ │ ├── 15.png
│ │ ├── 16.png
│ │ ├── 17.png
│ │ ├── 18.png
│ │ ├── 19.png
│ │ ├── 2.png
│ │ ├── 20.png
│ │ ├── 21.png
│ │ ├── 22.png
│ │ ├── 23.png
│ │ ├── 24.png
│ │ ├── 25.png
│ │ ├── 26.png
│ │ ├── 27.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ └── 9.png
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── A1_Introduction
│ ├── A1_Intro.Rmd
│ ├── A1_Intro.html
│ ├── A1_Intro.pdf
│ ├── images
│ │ └── camel_snake_kebab.jpg
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── twitter-widget
│ │ └── widgets.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── A2_The_YouTube_API
│ ├── A2_The_YouTube_API.Rmd
│ ├── A2_The_YouTube_API.html
│ ├── Automatic Sampling and Analysis of YouTube Data.pdf
│ ├── Images
│ │ ├── API-call-construction.png
│ │ ├── API_harvesting.png
│ │ ├── BBC2.png
│ │ ├── BBC_description.png
│ │ ├── BBC_descriptionHTML.png
│ │ ├── Metrics.png
│ │ ├── Odyssey4.jpg
│ │ ├── QuotaLimit_cut.png
│ │ ├── Quotas.png
│ │ ├── RSeleniumScreenshot.png
│ │ ├── RSeleniumScreenshot2.png
│ │ ├── RSeleniumScreenshot3.png
│ │ ├── RateLimitReduction.png
│ │ ├── RateLimitReduction2.png
│ │ ├── RateLimitReduction3.png
│ │ ├── Screenscraping.png
│ │ ├── Screenscraping_2.png
│ │ ├── YouTube_better.png
│ │ ├── YouTube_schematic2.png
│ │ ├── YouTube_schematic2_harvesting.png
│ │ └── YouTube_schematic2_scraping.png
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs-2.6
│ │ └── header-attrs.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css-0.0.1
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── A3_Collecting_data_with_tuber
│ ├── .gitignore
│ ├── A3_Collecting_data_with_the_tuber_package_for_R.Rmd
│ ├── A3_Collecting_data_with_the_tuber_package_for_R.html
│ ├── A3_Collecting_data_with_the_tuber_package_for_R.pdf
│ ├── images
│ │ ├── 23.png
│ │ ├── 24.png
│ │ ├── 25.png
│ │ ├── 26.png
│ │ └── yt_api_key.png
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── kePrint
│ │ └── kePrint.js
│ │ ├── lightable
│ │ └── lightable.css
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── twitter-widget
│ │ └── widgets.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── A4_Processing_and_cleaning_user_comments
│ ├── A4_Processing_and_Cleaning_User_Comments.Rmd
│ ├── A4_Processing_and_Cleaning_User_Comments.html
│ ├── A4_Processing_and_cleaning_user_comments.pdf
│ ├── Images
│ │ ├── DS1.jpg
│ │ ├── DS2.jpg
│ │ └── EmojiMovie.png
│ ├── libs
│ │ ├── clipboard
│ │ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ │ └── header-attrs.js
│ │ ├── remark-css
│ │ │ ├── default-fonts.css
│ │ │ └── default.css
│ │ ├── tile-view
│ │ │ ├── tile-view.css
│ │ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ │ ├── xaringanExtra-clipboard.css
│ │ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ │ └── xaringanExtra-extra-styles.css
│ └── workshop.css
├── B1_Basic_Text_Analysis
│ ├── B1_Basic_Text_Analysis.Rmd
│ ├── B1_Basic_Text_Analysis.html
│ ├── B1_Basic_Text_Analysis.pdf
│ ├── B1_Basic_Text_Analysis_files
│ │ └── figure-html
│ │ │ ├── cloudy-plot-1.png
│ │ │ ├── comments-over-time-plot-1.png
│ │ │ ├── cool-emoji-plot-1.png
│ │ │ ├── docfreq-plot-1.png
│ │ │ ├── emoji-barplot-1.png
│ │ │ └── word-freq-plot-1.png
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── twitter-widget
│ │ └── widgets.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── B2_Sentiment_Analysis_of_User_Comments
│ ├── B2_Sentiment_Analysis_of_User_Comments.Rmd
│ ├── B2_Sentiment_Analysis_of_User_Comments.html
│ ├── B2_Sentiment_Analysis_of_User_Comments.pdf
│ ├── B2_Sentiment_Analysis_of_User_Comments_files
│ │ └── figure-html
│ │ │ ├── unnamed-chunk-10-1.png
│ │ │ ├── unnamed-chunk-10-2.png
│ │ │ ├── unnamed-chunk-11-1.png
│ │ │ ├── unnamed-chunk-11-2.png
│ │ │ ├── unnamed-chunk-12-1.png
│ │ │ ├── unnamed-chunk-13-1.png
│ │ │ ├── unnamed-chunk-14-1.png
│ │ │ ├── unnamed-chunk-15-1.png
│ │ │ ├── unnamed-chunk-16-1.png
│ │ │ ├── unnamed-chunk-18-1.png
│ │ │ ├── unnamed-chunk-20-1.png
│ │ │ ├── unnamed-chunk-21-1.png
│ │ │ ├── unnamed-chunk-22-1.png
│ │ │ ├── unnamed-chunk-23-1.png
│ │ │ ├── unnamed-chunk-24-1.png
│ │ │ ├── unnamed-chunk-25-1.png
│ │ │ ├── unnamed-chunk-26-1.png
│ │ │ ├── unnamed-chunk-32-1.png
│ │ │ ├── unnamed-chunk-34-1.png
│ │ │ ├── unnamed-chunk-35-1.png
│ │ │ ├── unnamed-chunk-36-1.png
│ │ │ ├── unnamed-chunk-37-1.png
│ │ │ ├── unnamed-chunk-8-1.png
│ │ │ └── unnamed-chunk-9-1.png
│ ├── Images
│ │ ├── Emoji.png
│ │ ├── EmojiMisconstruals.png
│ │ ├── Encoding.png
│ │ ├── SAExample.png
│ │ └── all emoji differences.webp
│ ├── libs
│ │ ├── clipboard
│ │ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ │ └── header-attrs.js
│ │ ├── remark-css
│ │ │ ├── default-fonts.css
│ │ │ └── default.css
│ │ ├── tile-view
│ │ │ ├── tile-view.css
│ │ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ │ ├── xaringanExtra-clipboard.css
│ │ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ │ └── xaringanExtra-extra-styles.css
│ └── workshop.css
├── B3_Excursus_Retrieving_Video_Subtitles
│ ├── B3_Retrieving_Video_Subtitles.Rmd
│ ├── B3_Retrieving_Video_Subtitles.html
│ ├── B3_Retrieving_Video_Subtitles.pdf
│ ├── Images
│ │ └── youtubesubtitles.jpg
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
├── B4_Recap_Outlook_Practice
│ ├── B4_Recap_Outlook_Practice.Rmd
│ ├── B4_Recap_Outlook_Practice.html
│ ├── B4_Recap_Outlook_Practice.pdf
│ └── libs
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── header-attrs
│ │ └── header-attrs.js
│ │ ├── remark-css
│ │ ├── default-fonts.css
│ │ └── default.css
│ │ ├── tile-view
│ │ ├── tile-view.css
│ │ └── tile-view.js
│ │ ├── xaringanExtra-clipboard
│ │ ├── xaringanExtra-clipboard.css
│ │ └── xaringanExtra-clipboard.js
│ │ └── xaringanExtra-extra-styles
│ │ └── xaringanExtra-extra-styles.css
└── workshop.css
├── solutions
├── A2_The_Youtube_API_exercises.Rmd
├── A2_The_Youtube_API_exercises_solution.html
├── A3_tuber_exercises.Rmd
├── A3_tuber_exercises_solution.html
├── A4_Preprocessing_and_cleaning_data_exercises.Rmd
├── A4_Preprocessing_and_cleaning_data_exercises_solution.html
├── B1_Basic_text_analysis_exercises.Rmd
├── B1_Basic_text_analysis_exercises_solution.html
├── B2_Sentiment_analysis_of_user_comments_exercises.Rmd
├── B2_Sentiment_analysis_of_user_comments_exercises_solution.html
└── images
│ ├── APIRequests.png
│ └── Quotas.png
└── youtube-workshop-gesis-2022.Rproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # History files
2 | .Rhistory
3 | .Rapp.history
4 |
5 | # Session Data files
6 | .RData
7 |
8 | # User-specific files
9 | .Ruserdata
10 |
11 | # Example code in package build process
12 | *-Ex.R
13 |
14 | # Output files from R CMD build
15 | /*.tar.gz
16 |
17 | # Output files from R CMD check
18 | /*.Rcheck/
19 |
20 | # RStudio files
21 | .Rproj.user/
22 |
23 | # produced vignettes
24 | vignettes/*.html
25 | vignettes/*.pdf
26 |
27 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
28 | .httr-oauth
29 |
30 | # knitr and R markdown default cache directories
31 | *_cache/
32 | /cache/
33 |
34 | # Temporary files created by R markdown
35 | *.utf8.md
36 | *.knit.md
37 |
38 | # R Environment Variables
39 | .Renviron
40 |
41 | # data folder
42 | data/*
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use these materials, please cite them as follows."
3 | authors:
4 | - family-names: "Breuer"
5 | given-names: "Johannes"
6 | orcid: "https://orcid.org/0000-0001-5906-7873"
7 | - family-names: "Kohne"
8 | given-names: "Julian"
9 | - family-names: "Mohseni"
10 | given-names: "M. Rohangis"
11 | orcid: "https://orcid.org/0000-0001-7686-8322"
12 | title: "Workshop Automatic Sampling and Analysis of YouTube Comments"
13 | date-released: 2022-02-21
14 | url: "https://github.com/jobreu/youtube-workshop-gesis-2022"
15 | license: CC-BY-4.0
16 |
--------------------------------------------------------------------------------
/scripts/Authentication.R:
--------------------------------------------------------------------------------
1 | # Authentication
2 |
3 | # NB: Be careful to NOT share these credentials with anyone else whom you don't want to be able to use this account.
4 | appID <- "YourYouTubeClientID" # Insert your own Client ID here (OAuth 2.0 Client ID created for your project via the Google Developers platform)
5 | appSecret <- "YourYouTubeClientSecret" # Insert your own Client Secret here (Client key created for your project via the Google Developers platform)
6 |
7 | # Optional: authorization if you use the keyring package
8 | # creating the keyring
9 | library(keyring)
10 | keyring_create("YouTube", password = "YouTube") # This is the password of your keyring. You can replace it if you like.
11 | # saving your ID into the keyring
12 | appID <- "YourYouTubeClientID" # Insert your own Client ID here
13 | key_set_with_value("appSecret", appID, password = "YourYouTubeClientSecret", keyring = "YouTube") # insert your own Client Secret here
14 | keyring_unlock("YouTube")
15 | # getting the IDs from the keyring
16 | keyring_unlock(keyring = "YouTube", password = "YouTube") # Provide the password of the keyring here; alternatively, leave it empty here and you will be prompted for it
17 | appSecret <- key_get("appSecret", username = appID, keyring = "YouTube")
--------------------------------------------------------------------------------
/scripts/CamelCase.R:
--------------------------------------------------------------------------------
1 | # CamelCase function
2 |
3 | ## convert emoji names to CamelCase (if you want to learn (more) about CamelCase: https://en.wikipedia.org/wiki/Camel_case)
4 | simpleCap <- function(x) {
5 |
6 | # Splitting the string
7 | splitted <- strsplit(x, " ")[[1]]
8 |
9 | # Pasting it back together with capital letters
10 | paste(toupper(substring(splitted, 1,1)), substring(splitted, 2),sep = "", collapse = " ")
11 |
12 | }
--------------------------------------------------------------------------------
/scripts/ExtractEmoji.R:
--------------------------------------------------------------------------------
1 | # function for extracting Emoji from text
2 |
3 | ExtractEmoji <- function(x){
4 |
5 | SpacerInsert <- gsub(" ","[{[SpAC0R]}]", x)
6 | ExtractEmoji <- rm_between(SpacerInsert,"EMOJI_","[{[SpAC0R]}]", fixed = TRUE, extract = TRUE, clean = FALSE, trim = FALSE, include.markers = TRUE)
7 | UnlistEmoji <- unlist(ExtractEmoji)
8 | DeleteSpacer <- sapply(UnlistEmoji,function(x){gsub("[{[SpAC0R]}]"," ",x,fixed = TRUE)})
9 | names(DeleteSpacer) <- NULL
10 |
11 | Emoji <-paste0(DeleteSpacer,collapse = "")
12 | return(Emoji)
13 |
14 | }
--------------------------------------------------------------------------------
/scripts/ReplaceEmoji.R:
--------------------------------------------------------------------------------
1 | # Function for replacing emojis in the comments
2 |
3 | ReplaceEmoji <- function(x) {
4 |
5 | # install packages
6 |
7 | if ("remotes" %in% installed.packages() != TRUE) {
8 | install.packages("remotes")
9 | }
10 | if ("qdapRegex" %in% installed.packages() != TRUE) {
11 | install.packages("qdapRegex")
12 | }
13 | if ("emo" %in% installed.packages() != TRUE) {
14 | remotes::install_github("hadley/emo")
15 | }
16 |
17 | # load packages
18 | library(qdapRegex)
19 | library(emo)
20 |
21 | # source helper functions
22 | source("CamelCase.R")
23 |
24 | # import emoji list
25 | EmojiList <- jis
26 |
27 | CamelCaseEmojis <- lapply(jis$name, simpleCap)
28 | CollapsedEmojis <- lapply(CamelCaseEmojis, function(x){gsub(" ", "", x, fixed = TRUE)})
29 | EmojiList[,4]$name <- unlist(CollapsedEmojis)
30 |
31 | # order the list by the length of the string to avoid partial matching of shorter strings
32 | EmojiList <- EmojiList[rev(order(nchar(jis$emoji))),]
33 |
34 | # assign x to a new variable so we can save the progress in the for-loop (see below)
35 | New <- x
36 |
37 | # note: rm_default throws a warning on each iteration that we can ignore
38 | oldw <- getOption("warn")
39 | options(warn = -1)
40 |
41 | # cycle through the list and replace everything
42 | # we have to add clean = FALSE and trim = FALSE to avoid deleting whitespaces that are part of the pattern
43 |
44 | for (i in 1:dim(EmojiList)[1]){
45 |
46 | New <- rm_default(New, pattern = EmojiList[i,3],replacement = paste0("EMOJI_", EmojiList[i,4]$name, " "), fixed = TRUE, clean = FALSE, trim = FALSE)
47 |
48 | }
49 |
50 | # turn warning messages back on
51 | options(warn = oldw)
52 |
53 | # output result
54 | return(New)
55 |
56 | }
--------------------------------------------------------------------------------
/scripts/YouTubeAPIAccess.R:
--------------------------------------------------------------------------------
1 | #### NEVER SHARE THIS SCRIPT WITH YOUR CREDENTIALS STORED IN IT!
2 |
3 |
4 | #### Testing YouTube API Access
5 |
6 | # installing the tuber package if not installed already
7 | if("tuber" %in% installed.packages() != TRUE) {
8 | install.packages("tuber")
9 | }
10 |
11 | # loading tuber package
12 | library(Rcpp) # making extra sure this package is available
13 | library(tuber)
14 |
15 |
16 | # Your Credentials (NEVER SHARE THOSE!)
17 | ID <- "ENTER-API-ID-HERE"
18 | secret <- "ENTER-API-SECRET-HERE"
19 |
20 | # authentication
21 | yt_oauth(ID, secret, token ="")
22 |
23 | # You will be asked in the R-console to
24 | # save an access token: Select no
25 |
26 | # You will be sent back to your browser to log in
27 |
28 | ### testing if it works:
29 |
30 | # get statistics of https://www.youtube.com/watch?v=HluANRwPyNo
31 | get_stats(video_id="HluANRwPyNo")
32 |
33 | # Searching for specific keywords
34 | data <- yt_search(term = "web scraping R",
35 | published_after = "2019-01-01T00:00:00Z",
36 | published_before = "2021-01-01T00:00:00Z",
37 | simplify = TRUE)
38 |
39 | # Collecting comments
40 | comments <- get_all_comments(video_id = "iik25wqIuFo")
41 | commentThreads <- get_comment_threads(filter=c(video_id = "iik25wqIuFo"))
--------------------------------------------------------------------------------
/scripts/emoji_mapping_function.R:
--------------------------------------------------------------------------------
1 | # Notes:
2 | # This function requires that the following packages are installed:
3 | # - dplyr, stringr (both part of the tidyverse)
4 | # - hadley/emo & dill/emoGG from GitHub
5 | # The function has two arguments:
6 | # x = the data frame with the frequencies
7 | # n = the number of mappings to create
8 | # The data frame containing the frequencies should be created using the `textstat_frequency()` function from the `quanteda.textstats` package
9 | # The emoji names in the `feature` column of the frequency df should have the format "emoji_facewithtearsofjoy"
10 | # Please note that this code has not been tested systematically.
11 | # Depending on which emojis are the most frequent for the video you look at, this function might not work because
12 | # a) one of the emojis is not included in the emoji lookup table (which uses the `jis` data frame from the `emo` package or
13 | # b) the content in the `runes` column does not match the format/code that the `emoji` argument in the `geom_emoji` function from the `emoGG` package expects.
14 |
15 | create_emoji_mappings <- function(x, n) {
16 |
17 | emoji_lookup <- emo::jis %>%
18 | dplyr::select(runes, name) %>%
19 | dplyr::mutate(runes = stringr::str_to_lower(runes),
20 | name = stringr::str_to_lower(name)) %>%
21 | dplyr::mutate(name = stringr::str_replace_all(name, " ", "")) %>%
22 | dplyr::mutate(name = paste0("emoji_", name))
23 |
24 | library(emoGG)
25 |
26 | for(i in 1:n){
27 | name <- paste0("mapping", i)
28 | assign(name,
29 | do.call(geom_emoji,list(data = x[i,],
30 | emoji = gsub("^0{2}","", strsplit(tolower(emoji_lookup$runes[emoji_lookup$name == as.character(x[i,]$feature)])," ")[[1]][1]))), envir = .GlobalEnv)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/install_packages.R:
--------------------------------------------------------------------------------
1 | # check if packages are installed and install them if that is not the case
2 | packages <- c("remotes",
3 | "tuber",
4 | "tidyverse",
5 | "anytime",
6 | "qdapRegex",
7 | "vosonSML",
8 | "lexicon",
9 | "syuzhet",
10 | "ggcorrplot",
11 | "sentimentr",
12 | "quanteda",
13 | "quanteda.textstats",
14 | "wordcloud")
15 |
16 | install.packages(setdiff(packages,
17 | rownames(installed.packages())))
18 |
19 | # some packages need to be installed from GitHub
20 | library(remotes)
21 |
22 | install_github("hadley/emo")
23 | install_github("dill/emoGG")
24 | install_github("jooyoungseo/youtubecaption")
--------------------------------------------------------------------------------
/scripts/video_details_to_df.R:
--------------------------------------------------------------------------------
1 | # taken & adapted from the development version of tuber on Feb 22nd, 2022 (https://github.com/soodoku/tuber)
2 | # see this commit: https://github.com/soodoku/tuber/commit/42a8131098856c8aeb0fcbad70ed5f0e33b9652b
3 |
4 | conditional_unnest_wider <- function(data_input, var) {
5 | if (var %in% names(data_input)) {
6 | tidyr::unnest_wider(data_input, var, names_sep = "_")
7 | } else {
8 | data_input
9 | }
10 | }
11 |
12 |
13 | video_details_to_df <- function(res) {
14 | intermediate <- res %>%
15 | tibble::enframe() %>%
16 | tidyr::pivot_wider() %>%
17 | tidyr::unnest(cols = c(kind, etag)) %>%
18 | # reflect level of nesting in column name
19 | dplyr::rename(response_kind = kind, response_etag = etag) %>%
20 | tidyr::unnest(items) %>%
21 | tidyr::unnest_wider(col = items) %>%
22 | # reflect level of nesting in column name for those that may not be unique
23 | dplyr::rename(items_kind = kind, items_etag = etag) %>%
24 | tidyr::unnest_wider(snippet)
25 |
26 | intermediate_2 <- intermediate %>%
27 | # fields that may not be available:
28 | # live streaming details
29 | conditional_unnest_wider(var = "liveStreamingDetails") %>%
30 | # region restriction (rental videos)
31 | conditional_unnest_wider(var = "regionRestriction") %>%
32 | conditional_unnest_wider(var = "regionRestriction_allowed") %>%
33 | # statistics
34 | conditional_unnest_wider(var = "statistics") %>%
35 | # status
36 | conditional_unnest_wider(var = "status") %>%
37 | # player
38 | conditional_unnest_wider(var = "player") %>%
39 | # contentDetails
40 | conditional_unnest_wider(var = "contentDetails") %>%
41 | conditional_unnest_wider(var = "topicDetails") %>%
42 | conditional_unnest_wider(var = "localized") %>%
43 | conditional_unnest_wider(var = "pageInfo") %>%
44 | # thumbnails
45 | conditional_unnest_wider(var = "thumbnails") %>%
46 | conditional_unnest_wider(var = "thumbnails_default") %>%
47 | conditional_unnest_wider(var = "thumbnails_standard") %>%
48 | conditional_unnest_wider(var = "thumbnails_medium") %>%
49 | conditional_unnest_wider(var = "thumbnails_high") %>%
50 | conditional_unnest_wider(var = "thumbnails_maxres")
51 |
52 |
53 | intermediate_2
54 | }
--------------------------------------------------------------------------------
/scripts/yt_parse.R:
--------------------------------------------------------------------------------
1 | ## YouTube parsing Script
2 |
3 | # Note: This function will only work if the helper functions are in the same directory
4 | # (CamelCase.R;ExtractEmoji.R;ReplaceEmoji.R)
5 |
6 | yt_parse <- function(data){
7 |
8 | #### Setup
9 |
10 | # installing packages
11 | if ("remotes" %in% installed.packages() != TRUE) {
12 | install.packages("remotes")
13 | }
14 | if ("anytime" %in% installed.packages() != TRUE) {
15 | install.packages("anytime")
16 | }
17 | if ("qdapRegex" %in% installed.packages() != TRUE) {
18 | install.packages("qdapRegex")
19 | }
20 | if ("emo" %in% installed.packages() != TRUE) {
21 | remotes::install_github("hadley/emo")
22 | }
23 |
24 | # loading packages
25 | library(anytime)
26 | library(qdapRegex)
27 | library(emo)
28 |
29 | # sourcing helper functions
30 | source("CamelCase.R")
31 | source("ExtractEmoji.R")
32 | source("ReplaceEmoji.R")
33 |
34 | #### Data Preperation
35 |
36 | # accounting for dataframes without "parentId" column (those scraped with get_comments() instead of get_all_comments())
37 |
38 | if (is.null(data$parentId)) {
39 |
40 | parentId <- rep(NA,dim(data)[1])
41 | data <- cbind.data.frame(data,parentId)
42 |
43 | }
44 |
45 | # only keeping the relevant columns
46 | data <- data[,c("authorDisplayName","textOriginal","likeCount","publishedAt","updatedAt","parentId","id")]
47 |
48 | # convert dataframe columns to proper types
49 | data$authorDisplayName <- as.character(data$authorDisplayName)
50 | data$textOriginal <- as.character(data$textOriginal)
51 | data$likeCount <- as.numeric(data$likeCount)
52 | data$parentId <- as.character(data$parentId)
53 | data$id <- as.character(data$id)
54 | data$publishedAt <- anytime(data$publishedAt, asUTC = TRUE)
55 | data$updatedAt <- anytime(data$updatedAt, asUTC = TRUE)
56 |
57 | #### Emojis
58 |
59 | # Create a text column in which emojis are replaced by their textual descriptions
60 | # (This is handled by the helper function ReplaceEmoji)
61 | TextEmoRep <- ReplaceEmoji(data$textOriginal)
62 |
63 | # Create a text column in which emojis are deleted
64 | TextEmoDel <- ji_replace_all(data$textOriginal,"")
65 |
66 | # Create a column with only the textual descriptions of emojis for each comment
67 | Emoji <- sapply(TextEmoRep,ExtractEmoji)
68 |
69 | #### URLs
70 |
71 | # Extract URLs from comments
72 | Links <- rm_url(data$textOriginal, extract = TRUE)
73 | Links <- I(Links)
74 |
75 | #### Combine everything into one dataframe
76 | df <- cbind.data.frame(data$authorDisplayName,data$textOriginal,TextEmoRep,TextEmoDel,Emoji,data$likeCount,Links,data$publishedAt,data$updatedAt,data$parentId,data$id, stringsAsFactors = FALSE)
77 | names(df) <- c("Author","Text","TextEmojiReplaced","TextEmojiDeleted","Emoji","LikeCount","URL","Published","Updated","ParentId","CommentID")
78 | row.names(df) <- NULL
79 |
80 | #### return results
81 | return(df)
82 |
83 | }
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/A0_YouTubeAPISetup.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/A0_YouTubeAPISetup.pdf
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/0.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/1.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/10.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/11.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/12.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/13.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/14.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/15.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/16.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/17.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/18.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/19.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/2.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/20.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/21.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/22.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/23.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/24.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/25.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/26.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/27.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/3.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/4.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/5.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/6.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/7.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/8.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/Images/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobreu/youtube-workshop-gesis-2022/a5ff2a9e846ba359fab8c6313fda104606d0e960/slides/A0_YouTube_API_Setup/Images/9.png
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/header-attrs/header-attrs.js:
--------------------------------------------------------------------------------
1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to
2 | // be compatible with the behavior of Pandoc < 2.8).
3 | document.addEventListener('DOMContentLoaded', function(e) {
4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
5 | var i, h, a;
6 | for (i = 0; i < hs.length; i++) {
7 | h = hs[i];
8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
9 | a = h.attributes;
10 | while (a.length > 0) h.removeAttribute(a[0].name);
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/remark-css/default-fonts.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
2 | @import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
3 | @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700);
4 |
5 | body { font-family: 'Droid Serif', 'Palatino Linotype', 'Book Antiqua', Palatino, 'Microsoft YaHei', 'Songti SC', serif; }
6 | h1, h2, h3 {
7 | font-family: 'Yanone Kaffeesatz';
8 | font-weight: normal;
9 | }
10 | .remark-code, .remark-inline-code { font-family: 'Source Code Pro', 'Lucida Console', Monaco, monospace; }
11 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/remark-css/default.css:
--------------------------------------------------------------------------------
1 | a, a > code {
2 | color: rgb(249, 38, 114);
3 | text-decoration: none;
4 | }
5 | .footnote {
6 | position: absolute;
7 | bottom: 3em;
8 | padding-right: 4em;
9 | font-size: 90%;
10 | }
11 | .remark-code-line-highlighted { background-color: #ffff88; }
12 |
13 | .inverse {
14 | background-color: #272822;
15 | color: #d6d6d6;
16 | text-shadow: 0 0 20px #333;
17 | }
18 | .inverse h1, .inverse h2, .inverse h3 {
19 | color: #f3f3f3;
20 | }
21 | /* Two-column layout */
22 | .left-column {
23 | color: #777;
24 | width: 20%;
25 | height: 92%;
26 | float: left;
27 | }
28 | .left-column h2:last-of-type, .left-column h3:last-child {
29 | color: #000;
30 | }
31 | .right-column {
32 | width: 75%;
33 | float: right;
34 | padding-top: 1em;
35 | }
36 | .pull-left {
37 | float: left;
38 | width: 47%;
39 | }
40 | .pull-right {
41 | float: right;
42 | width: 47%;
43 | }
44 | .pull-right + * {
45 | clear: both;
46 | }
47 | img, video, iframe {
48 | max-width: 100%;
49 | }
50 | blockquote {
51 | border-left: solid 5px lightgray;
52 | padding-left: 1em;
53 | }
54 | .remark-slide table {
55 | margin: auto;
56 | border-top: 1px solid #666;
57 | border-bottom: 1px solid #666;
58 | }
59 | .remark-slide table thead th { border-bottom: 1px solid #ddd; }
60 | th, td { padding: 5px; }
61 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { background: #eee }
62 |
63 | @page { margin: 0; }
64 | @media print {
65 | .remark-slide-scaler {
66 | width: 100% !important;
67 | height: 100% !important;
68 | transform: scale(1) !important;
69 | top: 0 !important;
70 | left: 0 !important;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/tile-view/tile-view.css:
--------------------------------------------------------------------------------
1 | .remark__tile-view * {
2 | box-sizing: border-box;
3 | }
4 |
5 | .remark__tile-view {
6 | background: lightgray;
7 | position: relative;
8 | width: 100%;
9 | height: 100%;
10 | padding: 3em;
11 | font-size: 18px;
12 | box-sizing: border-box;
13 | overflow: scroll;
14 | }
15 |
16 | .remark__tile-view__header {
17 | text-align: center;
18 | }
19 |
20 | .remark__tile-view__tiles {
21 | display: grid;
22 | /* Set column width in JS */
23 | /* grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); */
24 | justify-items: center;
25 | }
26 |
27 | .remark__tile-view__tile {
28 | position: relative;
29 | margin: 0.5em;
30 | padding: 0.5em;
31 | }
32 |
33 | .remark__tile-view__slide-container {
34 | margin: 0 auto;
35 | }
36 |
37 | .remark__tile-view__tile--current {
38 | background: #ffd863;
39 | border: 5px solid #ffd863;
40 | margin: calc(0.5em - 5px);
41 | border-radius: 0;
42 | }
43 |
44 | .remark__tile-view__tile--seen {
45 | opacity: 0.5;
46 | }
47 |
48 | .remark__tile-view__tile:hover {
49 | /* background: #993d70; */
50 | background: #44bc96;
51 | opacity: 1;
52 | }
53 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/tile-view/tile-view.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Tile View for remark.js Slides
3 | *
4 | * Garrick Aden-Buie
5 | *
6 | * Inspired and converted to Vanilla JS from
7 | * https://github.com/StephenHesperus/remark-hook/
8 | *
9 | * Include after remarkjs slides are initialized.
10 | *
11 | */
12 |
13 | /* global slideshow */
14 | (function () {
15 | const ready = function (fn) {
16 | /* MIT License Copyright (c) 2016 Nuclei */
17 | /* https://github.com/nuclei/readyjs */
18 | const completed = () => {
19 | document.removeEventListener('DOMContentLoaded', completed)
20 | window.removeEventListener('load', completed)
21 | fn()
22 | }
23 | if (document.readyState !== 'loading') {
24 | setTimeout(fn)
25 | } else {
26 | document.addEventListener('DOMContentLoaded', completed)
27 | window.addEventListener('load', completed)
28 | }
29 | }
30 |
31 | ready(function () {
32 | const launchKey = 79 // keycode for O, used to enable tile view
33 |
34 | // Slides container
35 | const remarkSlideShow = document.querySelector('div.remark-slides-area')
36 |
37 | let tileView = document.querySelector('div.remark__tile-view')
38 | if (!tileView) {
39 | tileView = document.createElement('div')
40 | tileView.className = 'remark__tile-view'
41 | }
42 |
43 | const toggleElement = el => {
44 | el.style.display = el.style.display === 'none' ? '' : 'none'
45 | }
46 |
47 | function slideshowResize () {
48 | window.dispatchEvent(new Event('resize'))
49 | }
50 |
51 | const toggleTileView = function () {
52 | toggleElement(tileView)
53 | toggleElement(remarkSlideShow)
54 |
55 | if (tileView.style.display === 'none') {
56 | // tileView is now hidden, go to current slide
57 | slideshow.gotoSlide(tileVars.currentSlideIdx + 1)
58 |
59 | slideshow.resume()
60 | slideshowResize()
61 | } else {
62 | // store current slide index prior to launching tile-view
63 | tileVars.currentSlideIdx = slideshow.getCurrentSlideIndex()
64 |
65 | // set class on seen and current slide and scroll into view
66 | const tiles = tileView.querySelectorAll('.remark__tile-view__tile');
67 | [...tiles].forEach((tile, idx) => {
68 | tile.classList.toggle(
69 | 'remark__tile-view__tile--seen',
70 | idx < tileVars.currentSlideIdx
71 | )
72 | tile.classList.toggle(
73 | 'remark__tile-view__tile--current',
74 | idx === tileVars.currentSlideIdx
75 | )
76 | })
77 | tiles[tileVars.currentSlideIdx].scrollIntoView({
78 | behavior: 'smooth',
79 | block: 'center'
80 | })
81 |
82 | slideshow.pause()
83 | }
84 | }
85 |
86 | const createTileView = ({ minSize = 250, title = document.title } = {}) => {
87 | // Tile view header
88 | const h1 = document.createElement('h1')
89 | h1.className = 'remark__tile-view__header'
90 | h1.innerHTML = title
91 |
92 | tileView.appendChild(h1)
93 | const tiles = document.createElement('div')
94 | tiles.className = 'remark__tile-view__tiles'
95 | tileView.appendChild(tiles)
96 |
97 | // Clone slideshow
98 | const slidesArea = remarkSlideShow.cloneNode(true)
99 |
100 | // Calculate slide scale and tile container size
101 | const slideScaler = slidesArea.querySelector('.remark-slide-scaler')
102 | const slideWidth = parseFloat(slideScaler.style.width.replace('px', ''))
103 | const slideHeight = parseFloat(
104 | slideScaler.style.height.replace('px', '')
105 | )
106 | const scale = minSize / Math.min(slideWidth, slideHeight)
107 | let tileWidth = Math.round(slideWidth * scale)
108 | let tileHeight = Math.round(slideHeight * scale)
109 |
110 | // convert tileWidth/Height to em relative to base 18px (set in CSS)
111 | tileWidth = tileWidth / 18
112 | tileHeight = tileHeight / 18
113 |
114 | tiles.style.gridTemplateColumns = `repeat(auto-fill, minmax(${tileWidth}em, 1fr))`
115 |
116 | const slides = slidesArea.querySelectorAll('.remark-slide-container')
117 |
118 | slides.forEach((slide, slideIndex) => {
119 | let tile = document.createElement('template')
120 | tile.innerHTML = `
`
123 | tile = tile.content.firstChild
124 |
125 | const tileContainer = tile.querySelector(
126 | '.remark__tile-view__slide-container'
127 | )
128 | tileContainer.style.width = `${tileWidth}em`
129 | tileContainer.style.height = `${tileHeight}em`
130 |
131 | const thisSlideScaler = slide.querySelector('.remark-slide-scaler')
132 | thisSlideScaler.style.top = '0px'
133 | thisSlideScaler.style.left = '0px'
134 | thisSlideScaler.style.transform = `scale(${scale})`
135 | thisSlideScaler.parentElement.classList.add('remark-visible')
136 |
137 | slide.addEventListener('click', () => {
138 | tileVars.currentSlideIdx = slideIndex
139 | toggleTileView()
140 | })
141 |
142 | tileContainer.appendChild(slide)
143 | tiles.appendChild(tile)
144 | })
145 |
146 | document.body.appendChild(tileView)
147 | }
148 |
149 | const tileVars = {}
150 |
151 | document.addEventListener('keydown', ev => {
152 | if (ev.keyCode === launchKey) {
153 | toggleTileView()
154 | }
155 | })
156 |
157 | const addTileViewHelpText = () => {
158 | const helpTable = document.querySelector(
159 | '.remark-help-content table.light-keys'
160 | )
161 | if (!helpTable) {
162 | console.error(
163 | 'Could not find remark help table, has remark been initialized?'
164 | )
165 | return
166 | }
167 | const newRow = document.createElement('tr')
168 | newRow.innerHTML += 'o | '
169 | newRow.innerHTML += 'Tile View: Overview of Slides | '
170 | helpTable.append(newRow)
171 | }
172 |
173 | createTileView({ minSize: 200 })
174 | toggleElement(tileView)
175 | addTileViewHelpText()
176 | })
177 | })()
178 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/xaringanExtra-clipboard/xaringanExtra-clipboard.css:
--------------------------------------------------------------------------------
1 | .xaringanextra-clipboard-button {
2 | position: absolute;
3 | top: 0;
4 | right: 0;
5 | font-size: 0.8em;
6 | padding: 0.5em;
7 | display: none;
8 | background-color: transparent;
9 | border: none;
10 | opacity: 0.5;
11 | border-radius: 0;
12 | }
13 |
14 | .xaringanextra-clipboard-button:hover {
15 | background-color: rgba(0, 0, 0, 0.1);
16 | border: none;
17 | opacity: 1;
18 | }
19 |
20 | :hover > .xaringanextra-clipboard-button {
21 | display: block;
22 | transform: translateY(0);
23 | }
24 |
--------------------------------------------------------------------------------
/slides/A0_YouTube_API_Setup/libs/xaringanExtra-clipboard/xaringanExtra-clipboard.js:
--------------------------------------------------------------------------------
1 | /* global slideshow,window,document */
2 | window.xaringanExtraClipboard = function (selector, text) {
3 | if (!window.ClipboardJS.isSupported()) return
4 | if (!window.xaringanExtraClipboards) window.xaringanExtraClipboards = {}
5 |
6 | const ready = function (fn) {
7 | /* MIT License Copyright (c) 2016 Nuclei */
8 | /* https://github.com/nuclei/readyjs */
9 | const completed = () => {
10 | document.removeEventListener('DOMContentLoaded', completed)
11 | window.removeEventListener('load', completed)
12 | fn()
13 | }
14 | if (document.readyState !== 'loading') {
15 | setTimeout(fn)
16 | } else {
17 | document.addEventListener('DOMContentLoaded', completed)
18 | window.addEventListener('load', completed)
19 | }
20 | }
21 |
22 | ready(function () {
23 | const {
24 | button: buttonText = 'Copy Code',
25 | success: successText = 'Copied!',
26 | error: errorText = 'Press Ctrl+C to Copy'
27 | } = text
28 |
29 | const template = '`
31 |
32 | const isRemarkSlideshow = typeof slideshow !== 'undefined' &&
33 | Object.prototype.hasOwnProperty.call(slideshow, 'getSlides')
34 |
35 | let siblingSelector = selector || 'pre'
36 | if (!selector && isRemarkSlideshow) {
37 | siblingSelector = '.remark-slides-area ' + siblingSelector
38 | }
39 |
40 | // insert