Flux rss pour youtube (ou autre méthode)

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Flux rss pour youtube (ou autre méthode)

Message par SoPic » 23 Jan 2020 17:26

Bonjour tout le monde !

Je développe une appli dans laquelle je cherche à afficher la dernière vidéo youtube d'une chaîne.
Je me suis donc penché sur le flux rss et si j'en crois youtube j'ai juste à copier coller l'adresse de la chaîne dans mon agrégateur de flux rss...
Je n'ai rien trouvé de rassurant sur l'asset store (vieux et peu d'explication). Est-ce que quelqu'un a une piste pour développer ça ? Est-ce que je peux faire ça sous c# ou est-ce qu'il faut que je me tourne vers le php ? Est-ce que les flux rss sont dépassé et qu'il existe une autre méthode ?
Oui, j'en suis au stade des questions :gene:

Merci !

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Flux rss pour youtube (ou autre méthode)

Message par SoPic » 26 Jan 2020 18:59

Bon, j'avance doucement sur mon soucis.
Déjà j'ai enfin mis la main sur la bonne adresse pour récupérer le flux rss d'une chaîne.

Du coup j'ai quand même pris l'asset dans le store car j'avais tous les éléments pour. Vu comment il est construit, il me semble que ça colle bien avec ce que je veux. Mais ça marche pas :(

J'ai le message suivant : Object reference not set to an instance of an object
RSSReader+<GetXMLNodes>d__11.MoveNext () (at Assets/RSSFeeder/Code/MonoBehaviours/RSSReader.cs:122)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)

Je suis trop un noob pour voir d'où viens le soucis. Quelqu'un aurait-il une idée ?

Code : Tout sélectionner

using UnityEngine;
using System.Collections;
using System.Xml;
using UnityEngine.UI;
using System;

/// <summary>
/// This is the main class to read our RSS Feed.
/// </summary>
public class RSSReader : MonoBehaviour {
	// All In-Panel GameObjects.  You can make these public and change them in the component if you do.  Remove them from Awake() if so.
	private GameObject ItemTitleText;
	private GameObject ItemDescriptionText;
	private GameObject ArticleImage;

	// Synchronized arrays of things that may change via RSS.
	private RSSContentItem[] RSSItems;

	// Tells us what spot to go to in our current set of arrays.
	private int CurrentSitePlacement;
	private int CurrentItemPlacement;

	// Our list of URLS, set from the component.
	public string[] URLs;

	#region Initialization & Setup
	void Awake() {
		// Declare GameObjects.
		ItemTitleText = this.transform.Find("ArticleTitlePanel").Find ("TitleText").gameObject;
		ItemDescriptionText = this.transform.Find("DescriptionPanel").Find ("DescriptionText").gameObject;
		ArticleImage = this.transform.Find ("ArticleImage").gameObject;
	}

	void Start() {
		CurrentSitePlacement = 0;
		StartCoroutine (GetXMLNodes ());
	}
	#endregion

	#region Changing on-screen text
	/// <summary>
	/// Changes our current displayed text as appropriate, making sure we properly save our place.
	/// </summary>
	private void ChangeAndDisplayText (int currentItem) {
		// Make sure we aren't looking for other images, hide the blank image unless we do.
		StopAllCoroutines ();
		ArticleImage.SetActive (false);

		// Set our current place and get all applicable items.
		CurrentItemPlacement = currentItem;
		ItemTitleText.GetComponent<Text> ().text = RSSItems[CurrentItemPlacement].Title;
		ItemDescriptionText.GetComponent<Text> ().text = RSSItems[CurrentItemPlacement].Description;
		if (RSSItems[CurrentItemPlacement].Image != "") {
			if (RSSItems[CurrentItemPlacement].Image.Contains("youtube")) {
				StartCoroutine (LoadCurrentImage (RSSItems[CurrentItemPlacement].Image));
			} else {
				StartCoroutine (LoadCurrentImage (RSSItems[CurrentItemPlacement].Image));
			}
		}
	}

	/// <summary>
	/// Loads our current image if appropriate.
	/// </summary>
	private IEnumerator LoadCurrentImage (string imageText) {
		Debug.Log ("Image Loading");
		WWW imageLink = new WWW (imageText);
		yield return imageLink;
		Debug.Log ("Image Loaded");
		ArticleImage.SetActive (true);
		ArticleImage.GetComponent<RawImage> ().texture = imageLink.texture;
	}

	/// <summary>
	/// Loads our current video if appropriate.  Not currently in use, but can be set up should we wish to use it.
	/// </summary>
//	private IEnumerator LoadCurrentVideo(string imageText) {
//		Debug.Log ("Image Loading");
//		WWW imageLink = new WWW (imageText);
//		yield return imageLink;
//		Debug.Log ("Image Loaded");
//		ArticleImage.SetActive (true);
//		ArticleImage.GetComponent<RawImage> ().texture = imageLink.movie;
//	}
	#endregion

	/// <summary>
	/// The Co-Routine used for gathering and displaying our news.
	/// </summary>
	public IEnumerator GetXMLNodes() {
		// Cover up our news panel with a loading panel while we work our magic.
//		this.transform.FindChild ("LoadingPanel").gameObject.SetActive (true);

		// Reset Current Placement.
		CurrentItemPlacement = 0;

		// Create WWW Object via URL
		WWW xmlLink = new WWW (URLs[CurrentSitePlacement]);
		yield return xmlLink;

		// Wait to continue until we are done loading.
		if (xmlLink.isDone) {
			// Create a local instance of our XML Document
			XmlDocument currentXMLDoc = new XmlDocument ();

			// Load the entire XML document from our xmlLink WWW String
			try {
				currentXMLDoc.LoadXml (xmlLink.text);
			} catch {
				try {
					System.IO.StringReader stringReader = new System.IO.StringReader (xmlLink.text); stringReader.Read (); // skip BOM 
					currentXMLDoc.LoadXml (stringReader.ReadToEnd ());
				} catch {
					Debug.Log ("XML Not Loading.");
				}
			}

			// Use this one if the one above doesn't work right. Skips the initial BOM character.
			

			// Set our Site Title, so we know where we are.
			this.transform.Find("SiteTitlePanel").Find("SiteTitleText").GetComponent<Text>().text = currentXMLDoc.SelectSingleNode ("rss/channel/title").InnerText;

			// Get each Item Node, add them to an XML Node List.
			XmlNodeList xmlNodeList = currentXMLDoc.SelectNodes ("rss/channel/item");

			// Make sure that we have the same amount of RSS items as we have items in our XML Node List.
			RSSItems = new RSSContentItem[xmlNodeList.Count];
			
			// Break apart each XML Node and add it to our RSS Items List.
			for (int i = 0; i < xmlNodeList.Count; i++) {
				RSSContentItem tempItem = new RSSContentItem ();
				tempItem.Title = xmlNodeList[i].SelectSingleNode ("title").InnerText;
				tempItem.Image = "";
				if ((xmlNodeList[i].SelectSingleNode ("description") != null) && (xmlNodeList[i].SelectSingleNode ("description").InnerText != null)) {
					tempItem.Description = xmlNodeList[i].SelectSingleNode ("description").InnerText;
				} else {
					tempItem.Description = "";
				}
				if ((xmlNodeList[i].SelectSingleNode ("link") != null) && (xmlNodeList[i].SelectSingleNode ("link").InnerText != null)) {
					tempItem.Link = xmlNodeList[i].SelectSingleNode ("link").InnerText;
				} else {
					tempItem.Link = "";
				}

				// Look to see if we can find the content:encoded section.  Really don't like this implementation, but isn't coming up with SelectSingleNode.
				foreach (XmlNode item in xmlNodeList[i]) {
					if (item.Name == "content:encoded") {
						if (item.InnerText.Contains("img")) {
							tempItem.Image = item.InnerText.Split (new string[] { "src=\"" }, StringSplitOptions.None)[1].Split ('\"')[0].Trim ();
						} else if (item.InnerText.Contains ("youtube")) {
							Debug.Log ("http://img.youtube.com/vi" + item.InnerText.Split (new string[] { "embed" }, StringSplitOptions.None)[1].Split ('\"')[0].Trim ().Split ('?')[0].Trim () + "/0.jpg");
							tempItem.Image = "http://img.youtube.com/vi" + item.InnerText.Split (new string[] { "embed" }, StringSplitOptions.None)[1].Split ('\"')[0].Trim ().Split ('?')[0].Trim () + "/0.jpg";
						}
					}
				}
				RSSItems[i] = tempItem;
			}

			// Set up our text to show the first content we have.
			if (RSSItems.Length > 0) {
				ChangeAndDisplayText (CurrentItemPlacement);
			}

			// We have our items, take away the loading panel.
//			this.transform.FindChild ("LoadingPanel").gameObject.SetActive (false);
		}
	}

	#region External Buttons
	/// <summary>
	/// Changes the main RSS text.
	/// </summary>
	public void ChangeCurrentItem(bool increment) {
		// Is this being incremented?  If so, check to ensure we don't go larger than the array.
		if (increment) {
			if ((CurrentItemPlacement + 1) <= (RSSItems.Length - 1)) {
				CurrentItemPlacement += 1;
			} else {
				CurrentItemPlacement = 0;
			}
		} else {						// If not, make sure we don't try to go below 0.
			if ((CurrentItemPlacement - 1) >= 0) {
				CurrentItemPlacement -= 1;
			} else {
				CurrentItemPlacement = (RSSItems.Length - 1);
			}
		}

		ChangeAndDisplayText (CurrentItemPlacement);
	}

	/// <summary>
	/// User wants to read the article online.  Send them to the proper link.
	/// </summary>
	public void ReadMorePressed () {
		Application.OpenURL (RSSItems[CurrentItemPlacement].Link);
	}

	/// <summary>
	/// Changes the current site being displayed.
	/// </summary>
	public void ChangeCurrentSite (bool increment) {
		// Is this being incremented?  If so, check to ensure we don't go larger than the array.
		if (increment) {
			if ((CurrentSitePlacement + 1) <= (URLs.Length - 1)) {
				CurrentSitePlacement += 1;
			} else {
				CurrentSitePlacement = 0;
			}
		} else {                        // If not, make sure we don't try to go below 0.
			if ((CurrentSitePlacement - 1) >= 0) {
				CurrentSitePlacement -= 1;
			} else {
				CurrentSitePlacement = (URLs.Length - 1);
			}
		}

		StartCoroutine (GetXMLNodes ());
	}
	#endregion
}

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Flux rss pour youtube (ou autre méthode)

Message par SoPic » 30 Jan 2020 17:56

Bon, j'avance, doucement...
Déjà j'ai remis le WWW au goût du jour. Ensuite j'ai compris dans l'ensemble comment on lisait un fichier xml. Par contre quand je rentre les infos du journal Le Monde, ça marche bien, mais impossible d'accéder au titre de youtube X|

Voici un extrait du monde :

Code : Tout sélectionner

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Le Monde.fr - Actualités et Infos en France et dans le monde</title><description>Le Monde.fr - 1er site d’information. Les articles du journal et toute l’actualité en continu : International, France, Société, Economie, Culture, Environnement, Blogs ...</description><copyright>Le Monde - L’utilisation des flux RSS du Monde.fr est réservée à un usage strictement personnel, non professionnel et non collectif. Toute autre exploitation doit faire l’objet d’une autorisation et donner lieu au versement d’une rémunération. Contact : droitsdauteur@lemonde.fr</copyright><link>https://www.lemonde.fr/rss/une.xml</link><pubDate>Thu, 30 Jan 2020 14:44:57 +0000</pubDate><language>fr</language><atom:link href="https://www.lemonde.fr/rss/une.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Coronavirus chinois : un 5e cas confirmé en France, les rapatriements depuis la Chine débutent]]></title><pubDate>Wed, 29 Jan 2020 18:11:35 +0100</pubDate><description><![CDATA[Un premier avion français est parti dans la nuit de mercredi à jeudi à destination de Wuhan. L’OMS convoque de nouveau ce jeudi son comité d’urgence.]]></description><guid isPermaLink="true">https://www.lemonde.fr/planete/article/2020/01/29/coronavirus-en-chine-plusieurs-centaines-de-rapatriements-premiers-cas-au-proche-orient_6027700_3244.html</guid><link>https://www.lemonde.fr/planete/article/2020/01/29/coronavirus-en-chine-plusieurs-centaines-de-rapatriements-premiers-cas-au-proche-orient_6027700_3244.html</link><enclosure url="https://img.lemde.fr/2020/01/26/412/0/4947/2473/644/322/60/0/663bb4e_5045148-01-06.jpg" length="30374" type="image/jpeg"/></item>
Et voici un extrait de youtube :

Code : Tout sélectionner

<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
<link rel="self" href="http://www.youtube.com/feeds/videos.xml?channel_id=UCv_XFdavFYrAVqMP39l7Qbg"/>
<id>yt:channel:UCv_XFdavFYrAVqMP39l7Qbg</id>
<yt:channelId>UCv_XFdavFYrAVqMP39l7Qbg</yt:channelId>
<title>Furyu QC</title>
<link rel="alternate" href="https://www.youtube.com/channel/UCv_XFdavFYrAVqMP39l7Qbg"/>
<author>...</author>
<published>2012-09-01T02:10:54+00:00</published>
<entry>
<id>yt:video:OrKGTE9MmXg</id>
<yt:videoId>OrKGTE9MmXg</yt:videoId>
<yt:channelId>UCv_XFdavFYrAVqMP39l7Qbg</yt:channelId>
<title>...</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=OrKGTE9MmXg"/>
<author>...</author>
<published>2020-01-30T00:21:01+00:00</published>
<updated>2020-01-30T02:46:29+00:00</updated>
<media:group>
<media:title>
[SSA KOTZ][PvP] On continue a s’entrainer en GD legendaire
</media:title>
<media:content url="https://www.youtube.com/v/OrKGTE9MmXg?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
<media:thumbnail url="https://i4.ytimg.com/vi/OrKGTE9MmXg/hqdefault.jpg" width="480" height="360"/>
<media:description>
Rediffusion des matchs de GD et ma recherche de team pour le prochain jamir Make pour m aider: https://mmt.fans/URWk/ Ça ne coute rien et si je suis top 10 lors du retour de Dreamcatcher cela me fera énormément plaisir ^^ Les lives sont uniquement sur Twitch et les videos sur Youtube Twitch: https://www.twitch.tv/furyufr Yotube: https://www.youtube.com/channel/UCv_XFdavFYrAVqMP39l7Qbg Media sociaux Twitter:https://twitter.com/FuryuGaming Facebook https://www.facebook.com/Furyu-Gaming-107073820830873/ Envi de soutenir la chaine pour qu'elle continue à vous aider? Il existe plusieurs moyens: Dons: https://streamelements.com/furyuqc/tip Utip: https://utip.io/furyu N'oubliez pas également que si vous avez Amazon Prime vous bénéficier d'un abonnement Twitch Prime gratuit Chaines ou je stream: Jeudi live sur Soken Games https://www.twitch.tv/soken_games/ Dimanche live sur SigmaEsports https://www.twitch.tv/sigmaelectronicsports/
</media:description>
<media:community>
<media:starRating count="10" average="5.00" min="1" max="5"/>
<media:statistics views="330"/>
</media:community>
</media:group>
</entry>
Quand j'accède à rss/channel/title sur le monde j’obtiens bien le titre, par contre quand j'accède à feed/title sur youtube j'ai rien...
Voilà, si quelqu'un a une piste, je prends. Car là je sèche complètement :-|

Avatar de l’utilisateur
boubouk50
ModoGenereux
ModoGenereux
Messages : 6186
Inscription : 28 Avr 2014 11:57
Localisation : Saint-Didier-en-Bresse (71)

Re: Flux rss pour youtube (ou autre méthode)

Message par boubouk50 » 31 Jan 2020 09:13

Il y a plusieurs balises <title> dans l'extrait, ne serait-ce donc pas dû à la récupération d'une mauviase balise <title>?
Peut-être faut-il déterminer précisément quel <title> récupérer?
"Ce n'est pas en améliorant la bougie, que l'on a inventé l'ampoule, c'est en marchant longtemps."
Nétiquette du forum
Savoir faire une recherche
Apprendre la programmation

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Flux rss pour youtube (ou autre méthode)

Message par SoPic » 31 Jan 2020 16:16

Je ne pense pas car la première balise s'accède via feed/title (enfin normalement mais vu que ça marche pas...) et la deuxième via feed/entry/title
Le premier title étant le nom de la chaine et le 2eme le nom de la vidéo.

Mais merci, ça fait du bien d'avoir une première réponse ::d

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Flux rss pour youtube (ou autre méthode)

Message par SoPic » 31 Jan 2020 18:51

Bon, je suis en passe de résoudre mon soucis. J'expliquerai tout ici quand ce sera effectivement le cas.
Il me reste à réussir à afficher la vignette... tous les tutos que j'ai vu se font via le www, et ça ne marche pas avec UnityWebRequest.

J'ai mon url de l'image dans une string (https://i2.ytimg.com/vi/-t3EWHqMlT4/hqdefault.jpg)
Du coup, comment je fais pour afficher cette image dans une image sur Unity ?
Merci :)

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: Flux rss pour youtube (ou autre méthode)

Message par djulio74 » 31 Jan 2020 19:23

Si unity arrive a accéder a cette image via l'url et bien la reconnaitre comme image, pourquoi pas créer une nouvelle texture, faire un "redpixels" sur celle via url, et "setpixels" sur celle créée?

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: Flux rss pour youtube (ou autre méthode)

Message par Max » 31 Jan 2020 19:58

Bonsoir,

il me semble avoir vu dans le code que tu donnes plus haut une fonction LoadCurrentImage .
Cela ne le fait pas ?
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Flux rss pour youtube (ou autre méthode)

Message par SoPic » 01 Fév 2020 00:28

Yeeeeees !!! Enfin j'y suis !
Eh beh, ce ne fût pas simple. Pour le dernier soucis je m'en suis sorti simplement en remplaçant le composant Image par le composant RawImage. Du coup j'ai pu récupérer la texture et l'assigner directement.
Bref ! Topic résolu mais je vais dire comment pour les prochains qui passent. Déjà oubliez le code dont je suis parti, j'ai tout refais. J'avais besoin d'un truc simple donc j'ai fait un truc simple à savoir récupérer la dernière vidéo : titre, vignette et lien.

Structure :
  • Pour récupérer le flux rss d'une chaîne il faut utiliser cette adresse : https://www.youtube.com/feeds/videos.xm ... id=votreID
    Un UnityWebRequest pour allez chercher les infos et stocker ça dans une string
    A l'aide des string.IndexOf + string.Substring, isoler le titre, le lien de la vidéo et le lien de la vignette pour stocker ça dans 3 string différentes
    Assigner le titre à un UIText et appeler le lien avec une fonction public appelée avec un composant button
    Utiliser un deuxième UnityWebRequest pour allez chercher l'image et la stocker en tant que texture
    Utiliser un GetComponent pour récupérer le fameux RawImage et assigner la nouvelle texture
Bien entendu y'a des choses à peaufiner comme le ratio de l'image téléchargée, pour que ce soit plus propre...
Mais en l'état, ça fonctionne bien chez moi et j'ai testé avec plusieurs chaînes youtube.

Et voici le code :

Code : Tout sélectionner

public class StreamerFlux : MonoBehaviour
{
    private string rssString;
    
    // variables pour titre
    private int titleStartPos;
    private int startTitle;
    private int endTitle;
    private string videoTitle;

    // variables url video
    private int startVideo;
    private int endVideo;
    private string videoUrl;

    //variables url vignettes
    private int startVignette;
    private int endVignette;
    private string videoVignette;

    //gameObject d'affichage
    public Text TitreText;
    public GameObject VignetteImage;
    Texture2D VignetteTexture;

    
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(FluxYoutube());
    }

    // Update is called once per frame
    IEnumerator FluxYoutube()
    {
        UnityWebRequest www = UnityWebRequest.Get("https://www.youtube.com/feeds/videos.xml?channel_id=UCJX2jicMiWGnDurJwvyxkWg");
        yield return www.SendWebRequest();
		
        if (www.isNetworkError || www.isHttpError)
        {
            Debug.Log(www.error);            
        }
        else
        {
            rssString = www.downloadHandler.text;
		    Debug.Log (rssString);

            //recherche titre dernière video
            titleStartPos = rssString.IndexOf("<entry>");
            startTitle = rssString.IndexOf("<title>",titleStartPos);
            endTitle = rssString.IndexOf("</title>",titleStartPos);
            videoTitle = rssString.Substring(startTitle+=7,endTitle-=startTitle);
            TitreText.text = videoTitle;
            //Debug.Log(videoTitle);

            //recherche url video
            startVideo = rssString.IndexOf("https:",titleStartPos);
            endVideo = rssString.IndexOf("/>",startVideo);
            videoUrl = rssString.Substring(startVideo,endVideo-=startVideo+=1);
            //Debug.Log(videoUrl);

            //recherche url vignette
            startVignette = rssString.IndexOf("media:thumbnail",titleStartPos);
            startVignette = startVignette+=21;
            endVignette = rssString.IndexOf("width",startVignette);
            endVignette = endVignette-=2;
            videoVignette = rssString.Substring(startVignette,endVignette-=startVignette);
            StartCoroutine(VignetteLoading());
            //Debug.Log(videoVignette);
        }
    }

    IEnumerator VignetteLoading()
    {
        UnityWebRequest www2 = UnityWebRequest.Get(videoVignette);
        www2.downloadHandler = new DownloadHandlerTexture();
        yield return www2.SendWebRequest();
		
        if (www2.isNetworkError || www2.isHttpError)
        {
            Debug.Log(www2.error);
        }
        else
        {            
            VignetteTexture = DownloadHandlerTexture.GetContent(www2);
            VignetteImage.GetComponent<RawImage>().texture = VignetteTexture;
        }
    }

    public void OpenVideo(){
        Application.OpenURL(videoUrl);
    }

}
Je suis assez nouveau donc si vous voyez des optimisations ou des failles, n'hésitez pas ::d
Merci à tous !

Répondre

Revenir vers « Scripting »