Skip to content

Instantly share code, notes, and snippets.

@artzub
Last active March 17, 2024 16:28
Show Gist options
  • Save artzub/02b1b91e5c16a76bdf6c4710dbc105f0 to your computer and use it in GitHub Desktop.
Save artzub/02b1b91e5c16a76bdf6c4710dbc105f0 to your computer and use it in GitHub Desktop.

Revisions

  1. artzub revised this gist Feb 5, 2024. 1 changed file with 23 additions and 13 deletions.
    36 changes: 23 additions & 13 deletions snippet.js
    Original file line number Diff line number Diff line change
    @@ -1,29 +1,39 @@
    (function addAll() {
    const targetRootId = 'contents';

    const lastVideoSelector = 'ytd-rich-grid-row:last-of-type ytd-rich-item-renderer:last-child';
    const allVideosSelector = `.ytd-rich-grid-renderer#${targetRootId} ytd-rich-grid-media`;
    const rootNodeSelector = `ytd-rich-grid-renderer > #${targetRootId}`;

    const delay = (ms = 100) => new Promise((resolve) => {
    setTimeout(() => {
    resolve(true);
    }, ms)
    });


    (async function() {
    let neededVideoId = prompt('Enter id of the last video', '');

    if (!neededVideoId) {
    alert('The last vidoe id can not be empty');
    return;
    }

    const delay = (ms = 100) => new Promise((resolve) => {
    setTimeout(() => {
    resolve(true);
    }, ms)
    });

    const checkWatched = async () => {
    await delay(3e3);

    console.log('find video by id:', neededVideoId);
    const found = document.querySelector(`a[href^="/watch?v=${neededVideoId}"]`);

    if (found) {
    if (found) {
    console.log('found, stop observer');
    observer.disconnect();
    stageSecond();
    return;
    }

    const lastVideo = document.querySelector('ytd-item-section-renderer:last-of-type ytd-grid-video-renderer:last-child');
    const lastVideo = document.querySelector(lastVideoSelector);
    console.log('last video:', lastVideo);
    if (lastVideo) {
    lastVideo.scrollIntoView();
    }
    @@ -38,7 +48,7 @@
    };

    const stageSecond = async () => {
    let videos = Array.from(document.querySelectorAll('#items > ytd-grid-video-renderer'));
    let videos = Array.from(document.querySelectorAll(allVideosSelector));

    if (neededVideoId) {
    const index = videos.findIndex(item => item.data.videoId === neededVideoId);
    @@ -58,10 +68,10 @@
    `;
    };

    const itemsContainer = document.querySelector('ytd-section-list-renderer');
    const itemsContainer = document.querySelector(rootNodeSelector);

    const mutationListener = (mutationList, observer) => {
    const list = mutationList.filter(item => item.target.id === 'items');
    const list = mutationList.filter(item => item.target.id === targetRootId);

    if (list.length > 0) {
    runWaiter();
    @@ -72,5 +82,5 @@

    observer.observe(itemsContainer, { childList: true, subtree: true });

    checkWatched();
    await checkWatched();
    })();
  2. artzub revised this gist Oct 20, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion snippet.js
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@
    const checkWatched = async () => {
    await delay(3e3);

    const found = document.querySelector(`a[href="/watch?v=${neededVideoId}"]`);
    const found = document.querySelector(`a[href^="/watch?v=${neededVideoId}"]`);

    if (found) {
    observer.disconnect();
  3. artzub revised this gist Dec 13, 2021. 1 changed file with 76 additions and 0 deletions.
    76 changes: 76 additions & 0 deletions snippet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    (function addAll() {
    let neededVideoId = prompt('Enter id of the last video', '');

    if (!neededVideoId) {
    alert('The last vidoe id can not be empty');
    return;
    }

    const delay = (ms = 100) => new Promise((resolve) => {
    setTimeout(() => {
    resolve(true);
    }, ms)
    });

    const checkWatched = async () => {
    await delay(3e3);

    const found = document.querySelector(`a[href="/watch?v=${neededVideoId}"]`);

    if (found) {
    observer.disconnect();
    stageSecond();
    return;
    }

    const lastVideo = document.querySelector('ytd-item-section-renderer:last-of-type ytd-grid-video-renderer:last-child');
    if (lastVideo) {
    lastVideo.scrollIntoView();
    }
    };

    let timer;
    const runWaiter = () => {
    if (timer) {
    clearTimeout(timer);
    }
    timer = setTimeout(checkWatched, 2e3);
    };

    const stageSecond = async () => {
    let videos = Array.from(document.querySelectorAll('#items > ytd-grid-video-renderer'));

    if (neededVideoId) {
    const index = videos.findIndex(item => item.data.videoId === neededVideoId);
    videos.splice(index, videos.length);
    }

    videos = videos
    .filter(item => !item.data.isWatched)
    .reverse()
    .map(video => `<tr><td>${video.data.videoId}</td></tr>`)
    ;

    document.body.innerHTML = `
    <pre style="background: white; color: black">
    ${videos.join('\n')}
    </pre>
    `;
    };

    const itemsContainer = document.querySelector('ytd-section-list-renderer');

    const mutationListener = (mutationList, observer) => {
    const list = mutationList.filter(item => item.target.id === 'items');

    if (list.length > 0) {
    runWaiter();
    }
    };

    const observer = new MutationObserver(mutationListener);

    observer.observe(itemsContainer, { childList: true, subtree: true });

    checkWatched();
    })();
  4. artzub created this gist Dec 13, 2021.
    157 changes: 157 additions & 0 deletions app-script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,157 @@
    function findPlaylist(compare) {
    let pageToken;
    while(true) {
    const list = YouTube.Playlists.list(['snippet'], {
    "maxResults": 50,
    "mine": true,
    ...(pageToken && { pageToken })
    });

    const item = list.items.find((row) => compare && compare(row));
    if (item) {
    return item;
    }

    if (!list.nextPageToken) {
    return null;
    }

    pageToken = list.nextPageToken;
    }
    }

    function addPlaylist() {
    const title = `wl__${(new Date().getMonth()) + 1}`;

    const item = findPlaylist(({ snippet }) => snippet.title === title);

    if (item) {
    return item;
    }

    return YouTube.Playlists.insert(
    {
    'snippet': {
    title,
    },
    'status': {
    'privacyStatus': 'private'
    }
    },
    'snippet,status'
    );
    }

    function insertVideo(playlistId, videoId) {
    return YouTube.PlaylistItems.insert(
    {
    "snippet": {
    playlistId,
    "resourceId": {
    "kind": "youtube#video",
    videoId
    }
    }
    },
    'snippet'
    );
    }

    function run() {
    const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = spreadsheet.getSheets()[0];

    const lastRow = sheet.getLastRow();

    console.log({ lastRow });

    if (lastRow < 1) {
    return;
    }

    // 199 because 1 request for insterting playlist
    const lastIndex = lastRow > 199 ? 199 : lastRow;

    console.log(`get range: A1:A${lastIndex}`);

    spreadsheet.toast(`get range: A1:A${lastIndex}`, "fill playlist");

    let range = sheet.getRange(`A1:A${lastIndex}`);

    const rows = range.getValues().flat().filter(Boolean);

    console.log({ willProcessing: rows.length });

    if (rows.length) {
    let lastAdded = 0;
    let error;
    let lastVideo;
    try {
    const pl = addPlaylist();
    rows.forEach((videoId, index, arr) => {
    if (!videoId) {
    return;
    }

    try {
    insertVideo(pl.id, videoId, arr.length - index);
    lastVideo = videoId;
    }
    catch(err) {
    if (!err.message.includes('Video not found')) {
    throw err;
    }

    console.log('video not found', videoId);
    lastVideo = videoId;
    }
    lastAdded = index + 1;
    });
    } catch(err) {
    error = err;
    }

    console.log({ lastAdded, lastVideo });

    if (lastAdded > 0) {
    if (!lastVideo) {
    lastVideo = sheet.getRange('B1:B1').getValue();
    }

    const row = sheet.getLastRow() || 1;

    if (lastAdded >= row) {
    lastAdded = row - 1;
    }

    console.log({ row, lastAdded });

    if (lastAdded > 0) {
    sheet.deleteRows(1, lastAdded);
    } else {
    sheet.getRange('A1:A1').setValue('');
    }

    sheet.getRange('B1:B1').setValue(lastVideo);
    }

    if (error) {
    throw error;
    }
    }
    }

    function init() {
    const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    spreadsheet.removeMenu('YouTube');
    spreadsheet.addMenu('YouTube', [
    {
    name: 'fill playlist',
    functionName: 'run',
    }
    ]);
    }

    function onOpen() {
    init();
    }