• Community Manager

    @overclokk vai, riusciamo a partire con questa disposizione?

    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    @mirkomassarutto ti darà una mano nelle API giusto?

    L'idea che invece prospettare Mirko, è una figata. Ma ci facciamo un'altra cosa se lui ha voglia 🙂


    overclokk 1 Risposta
  • User Attivo

    Allora.. io ho fatto un test con Google Sheets

    Questo il codice dello script per importarsi i JSON

    /**
     * Retrieves all the rows in the active spreadsheet that contain data and logs the
     * values for each row.
     * For more information on using the Spreadsheet API, see
     * https://developers.google.com/apps-script/service_spreadsheet
     */
    function readRows() {
      var sheet = SpreadsheetApp.getActiveSheet();
      var rows = sheet.getDataRange();
      var numRows = rows.getNumRows();
      var values = rows.getValues();
    
      for (var i = 0; i <= numRows - 1; i++) {
        var row = values*;
        Logger.log(row);
      }
    };
    
    
    function onOpen() {
      var sheet = SpreadsheetApp.getActiveSpreadsheet();
      var entries = [{
        name : "Read Data",
        functionName : "readRows"
      }];
      sheet.addMenu("Script Center Menu", entries);
    };
    
    
    function ImportJSON(url, query, options) {
      return ImportJSONAdvanced(url, query, options, includeXPath_, defaultTransform_);
    }
    
    
    function ImportJSONAdvanced(url, query, options, includeFunc, transformFunc) {
      var jsondata = UrlFetchApp.fetch(url);
      var object   = JSON.parse(jsondata.getContentText());
      
      return parseJSONObject_(object, query, options, includeFunc, transformFunc);
    }
    
    
    function URLEncode(value) {
      return encodeURIComponent(value.toString());  
    }
    
    
    function parseJSONObject_(object, query, options, includeFunc, transformFunc) {
      var headers = new Array();
      var data    = new Array();
      
      if (query && !Array.isArray(query) && query.toString().indexOf(",") != -1) {
        query = query.toString().split(",");
      }
      
      if (options) {
        options = options.toString().split(",");
      }
        
      parseData_(headers, data, "", 1, object, query, options, includeFunc);
      parseHeaders_(headers, data);
      transformData_(data, options, transformFunc);
      
      return hasOption_(options, "noHeaders") ? (data.length > 1 ? data.slice(1) : new Array()) : data;
    }
    
    
    function parseData_(headers, data, path, rowIndex, value, query, options, includeFunc) {
      var dataInserted = false;
      
      if (isObject_(value)) {
        for (key in value) {
          if (parseData_(headers, data, path + "/" + key, rowIndex, value[key], query, options, includeFunc)) {
            dataInserted = true; 
          }
        }
      } else if (Array.isArray(value) && isObjectArray_(value)) {
        for (var i = 0; i < value.length; i++) {
          if (parseData_(headers, data, path, rowIndex, value*, query, options, includeFunc)) {
            dataInserted = true;
            rowIndex++;
          }
        }
      } else if (!includeFunc || includeFunc(query, path, options)) {
        // Handle arrays containing only scalar values
        if (Array.isArray(value)) {
          value = value.join(); 
        }
        
        // Insert new row if one doesn't already exist
        if (!data[rowIndex]) {
          data[rowIndex] = new Array();
        }
        
        // Add a new header if one doesn't exist
        if (!headers[path] && headers[path] != 0) {
          headers[path] = Object.keys(headers).length;
        }
        
        // Insert the data
        data[rowIndex][headers[path]] = value;
        dataInserted = true;
      }
      
      return dataInserted;
    }
    
    
    function parseHeaders_(headers, data) {
      data[0] = new Array();
    
      for (key in headers) {
        data[0][headers[key]] = key;
      }
    }
    
    
    function transformData_(data, options, transformFunc) {
      for (var i = 0; i < data.length; i++) {
        for (var j = 0; j < data*.length; j++) {
          transformFunc(data, i, j, options);
        }
      }
    }
    
    
    function isObject_(test) {
      return Object.prototype.toString.call(test) === '[object Object]';
    }
    
    
    function isObjectArray_(test) {
      for (var i = 0; i < test.length; i++) {
        if (isObject_(test*)) {
          return true; 
        }
      }  
    
      return false;
    }
    
    
    function includeXPath_(query, path, options) {
      if (!query) {
        return true; 
      } else if (Array.isArray(query)) {
        for (var i = 0; i < query.length; i++) {
          if (applyXPathRule_(query*, path, options)) {
            return true; 
          }
        }  
      } else {
        return applyXPathRule_(query, path, options);
      }
      
      return false; 
    };
    
    
    function applyXPathRule_(rule, path, options) {
      return path.indexOf(rule) == 0; 
    }
    
    
    function defaultTransform_(data, row, column, options) {
      if (!data[row][column]) {
        if (row < 2 || hasOption_(options, "noInherit")) {
          data[row][column] = "";
        } else {
          data[row][column] = data[row-1][column];
        }
      } 
    
      if (!hasOption_(options, "rawHeaders") && row == 0) {
        if (column == 0 && data[row].length > 1) {
          removeCommonPrefixes_(data, row);  
        }
        
        data[row][column] = toTitleCase_(data[row][column].toString().replace(/[\/\_]/g, " "));
      }
      
      if (!hasOption_(options, "noTruncate") && data[row][column]) {
        data[row][column] = data[row][column].toString().substr(0, 256);
      }
    
      if (hasOption_(options, "debugLocation")) {
        data[row][column] = "[" + row + "," + column + "]" + data[row][column];
      }
    }
    
    
    function removeCommonPrefixes_(data, row) {
      var matchIndex = data[row][0].length;
    
      for (var i = 1; i < data[row].length; i++) {
        matchIndex = findEqualityEndpoint_(data[row][i-1], data[row]*, matchIndex);
    
        if (matchIndex == 0) {
          return;
        }
      }
      
      for (var i = 0; i < data[row].length; i++) {
        data[row]* = data[row]*.substring(matchIndex, data[row]*.length);
      }
    }
    
    
    function findEqualityEndpoint_(string1, string2, stopAt) {
      if (!string1 || !string2) {
        return -1; 
      }
      
      var maxEndpoint = Math.min(stopAt, string1.length, string2.length);
      
      for (var i = 0; i < maxEndpoint; i++) {
        if (string1.charAt(i) != string2.charAt(i)) {
          return i;
        }
      }
      
      return maxEndpoint;
    }
      
    
    
    function toTitleCase_(text) {
      if (text == null) {
        return null;
      }
      
      return text.replace(/\w\S*/g, function(word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); });
    }
    
    
    function hasOption_(options, option) {
      return options && options.indexOf(option) >= 0;
    }
    

    A questo punto sono 3 i tipi di chiamata per ottenere i dati che ci interessano:

    =ImportJSON("https://youtube.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&forUsername=giorgiotave&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&id=UCjL9qnLkh-tLCNHlHA80vkw&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCjL9qnLkh-tLCNHlHA80vkw&maxResults=2&order=date&key=MYAPYKEY")
    
    =ImportJSON("https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id=1mMuAxzYM_0&key=MYAPYKEY")
    

    Il primo individua il ChannelID in base alla username (in alternativa, se conosciamo già il channel ID si può usare il secondo)
    Il Terzo serve per ottenere lD dei video pubblicati
    Il quarto fornisce i dettagli per ogni video


    overclokk 1 Risposta
  • Moderatore

    @giorgiotave ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    vai, riusciamo a partire con questa disposizione?
    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    Riesco a guardarci questo fine settimana, non prima.


    mirkomassarutto giorgiotave 2 Risposte
  • User Attivo

    @overclokk ti ho mandato un link in chat 😉


  • Moderatore

    @mirkomassarutto In Google script c'è anche già lAPI per collegare con Youtube senza leggere il json e senza aggiungere app key, Giorgio aggiungerà lo script nel suo account e il gioco è fatto.


    mirkomassarutto 1 Risposta
  • User Attivo

  • Community Manager

    @overclokk ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    @giorgiotave ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    vai, riusciamo a partire con questa disposizione?
    Così faccio partire la classe, appena ho almeno il form, faccio partire il tutto.

    Riesco a guardarci questo fine settimana, non prima.

    Perfetto 🙂

    GRANDI


  • Moderatore

    Eccomi, allora procediamo così:

    @giorgiotave dovresti nel tuo google drive creare una cartella dedicata (serve solo per tenere separato il progetto dal resto dei tuoi file) e poi dare accesso a me e @mirkomassarutto (ti passiamo la mail in chat) così creiamo direttamente il form e lo spreadsheet collegato.

    I form sono pubblici conoscendone la url, quello che si può fare è limitare a 1 risposta per utente, gli utenti per rispondere devono avere un account google.

    Poi per limitare ulteriormente sarebbe utile metterlo visibile sono per gli iscritti del forum?

    Al momento l'unica validazione che ho scritto è quella se l'ID del canale è corretta.

    L'ID del canale è l'unico mezzo sicuro che ci consente di reperire le info (non cambia mai come il nome), ho fatto delle prove con il nome del canale e non funziona con tutti quelli che ho provato (non sono stato a impazzire per capire il perché), poi, raga, se uno vuole fare lo youtuber dovrà imparare anche quale sia l'ID del proprio canale 😀

    Nello spreadsheet collegato al form meglio salvare solo le informazioni attuali, tutti gli altri calcoli (i vari salti iscritti, video, ecc) si fanno in un'altro foglio perché poi si dovra creare il "database" con lo storico ma un passo alla volta.


    giorgiotave 1 Risposta
  • Community Manager

    @overclokk fatto, passatemi le email 🙂

    GRANDISSIMI


  • Moderatore

    Intanto ho caricato i file del form e il foglio di calcolo


  • Moderatore

    Al momento solo l'ID è obbligatorio, finché facciamo dei test va bene così che se no si impazzisce a compilare tutti i campi.

    Per eseguire lo script al submit ho aggiunto un "attivatore" nell'editor degli scripts

    Il modula salva nel foglio "iscritti", poi il resto dei dati usiamo gli altri fogli.

    Ora procediamo con i test per vedere come e cosa migliorare nel form.


    giorgiotave 1 Risposta
  • Community Manager

    @overclokk va bene!

    Che devo fare?


  • Moderatore

    Rompilo 😂

    Nel senso vedi se compilandolo c'è qualcosa che non va, cancella le righe nel foglio di calcolo, cancella le risposte dal form e ripeti, così vediamo se c'è qualche problema.

    L'unico check che ho messo nel codice è solo per verificare che il canale esiste, non c'è nessun controllo se il canale è già stato inserito e non ci sono ancora le notice da mostrare in caso di errore.


  • Moderatore

    Intanto ho visto che forse si riescono a fare anche gli unit test, sarebbe interessante.


  • Community Manager

    FIGATA :d:
    FUNZIONA!

    Poi facciamo un tutorial ahhahahahaha


  • Moderatore

    Materiale giusto giusto per il nuovo canale 😎


  • Community Manager

    😎 😎 😎

    Comunque quando voi mi date il via io faccio il primo video di Road To 10.000 🙂

    Così in quello spiego come iscriversi 🙂


  • Moderatore

    Per ora di campo obbligatorio c'è solo ID canale, mettiamo obbligatori anche gli altri?

    Nei dati salvati ho aggiunto anche la data di pubblicazione del canale essendo anche quello un dato che non cambia mai.


    giorgiotave 1 Risposta
  • Moderatore

    Ho aggiunto anche il limite ad una risposta per account, così non importa neanche fare il check dei doppioni (in teoria).


  • Community Manager

    @overclokk ha detto in API di YouTube: riusciamo a fare un sito per piccole statistiche?:

    Per ora di campo obbligatorio c'è solo ID canale, mettiamo obbligatori anche gli altri?

    Sì vai 🙂

    Facciamo anche quelli. Io se ho l'ok parto questa settimana o la prossima 🙂