# Ext: sg_youtube <img alt="" src="https://www.sgalinski.de/typo3conf/ext/project_theme/Resources/Public/Images/logo.svg" /> License: [GNU GPL, Version 2](https://www.gnu.org/licenses/gpl-2.0.html) Repository: https://gitlab.sgalinski.de/typo3/sg_youtube Please report bugs here: https://gitlab.sgalinski.de/typo3/sg_youtube ## Installation / Integration First install the extension and activate it in the Extension Manager. ### TypoScript integration - Include the TypoScript in Configuration/TypoScript/setup.typoscript and constants.typoscript in your theme. - Add your YouTube API key: ```typoscript plugin.tx_sgyoutube { settings { # cat=plugin.tx_sgyoutube/file; type=string; label=YouTube API Key apiKey = <your-api-key> } } ``` ### Registration for more than the free 10.000 quotas per day It's not 1 quota per 1 API call. Each API has its own costs, which can be seen in the link below. Currently, at the version 3.2.1 we are using the following APIs: - "search/list" for channel videos - "playlistItems/list" for videos from a specific playlist - "videos/list" for getting the details for each video and the localizations, if needed. The maximum quota costs would be "102" at the moment for rendering the latest videos from a channel with the video details and translations. [Quota Calculator](https://developers.google.com/youtube/v3/determine_quota_cost) [YouTube API Services - Audit and Quota Extension Form](https://support.google.com/youtube/contact/yt_api_form?hl=en) #### Caching behaviour Because of the quota costs we implemented a caching for the calls for each day. The response from the APIs will be saved and used for 24 hours. Normally the site cache would do it, but it could be, that the cache will be cleared multiple times in a row, or that the plugin is on an uncached page. The TYPO3 registry is used as a cache. The cleanup is handled on the fly. If the `?disableYoutubeCache=1` parameter is added to the URL, this cache will be ignored as well. #### Possible way to solve the quota limit, if it's still reached You can use a different API key for specific sites. You can implement a TypoScript page uid check and just change the key from the "TypoScript integration" topic. ### Making changes in JavaScript/CSS We are shipping the extension with source files and already minified assets. By default, the minified assets are loaded in the Layout, so that the extension works out of the box just with plug and play. Should you want to change this behavior, you can do the following: - Override the layout path in TypoScript local/project_theme/Configuration/TypoScript/Extensions/SgYouTube/Constants.typoscript ``` plugin.tx_sgyoutube { settings { apiKey = MY_KEY } view { layoutRootPath = EXT:project_theme/Resources/Private/Layouts/SgYouTube/ } } ``` - Create a new layout file omitting the assets that you would like to change (for example, loading without CSS) ``` <div class="tx-sg-youtube"> <f:asset.script identifier="sgVideoJs" src="EXT:sg_youtube/Resources/Public/JavaScript/Dist/main.bundled.min.js" /> <f:render section="main"/> </div> ``` - Import the CSS or JavaScript source files in your respective asset pipeline and add them externally. ### Compiling CSS/JS assets with SGC - Install the sgalinski/sgc-core library via composer - Add the sg-youtube extension paths in the .sgc-config.json - Remove the loading of the compiled CSS/JS assets from Resources/Private/Templates/Youtube/Index.html - Add import the scss and js module file in the corresponding main.scss and main.js - Initialize the javascript modules in your main.js: ` new SgVideoLightbox(); SgVideo.initDefault();` - If you want to recompile the JS with SGC, you must add `excludeFromQa: ['!**/Backend/**/*']` to your .sgc-config.js and also set your main extension in extensions to sg-youtube `extensions: ['sg-youtube']` ### Compiling SASS only without SGC #### Example: `npm install -g sass` `npx sass ./Resources/Public/Sass/Bootstrap5/main.scss ./Resources/Public/StyleSheets/Bootstrap5/main.min.css --style compressed --no-source-map` ### Using the Bootstrap 5 templates If you want to use the Bootstrap 5 templates, you have to first install Bootstrap 5 in your theme to use its styles and JavaScript. Afterwards simply set the `plugin.tx_project_theme.config.bootstrapVersion` TypoScript setup variable to 5. ## Using Events to Customize YouTube API Results in TYPO3 This documentation explains how to leverage custom events in your TYPO3 extension to manipulate the results of YouTube API calls. By using these events, you can modify the API parameters, filter results, and further customize the JSON data returned from YouTube. ### Available Events The following events are dispatched in the YouTube video rendering process: 1. `BeforeYoutubeCallEvent` 2. `AfterYoutubeCallEvent` 3. `AfterFilterVideosEvent` 4. `AfterMapCustomThumbnailsEvent` ### Event Listeners #### 1. BeforeYoutubeCallEvent **Description**: This event is triggered before making the YouTube API call, allowing you to modify the API parameters. ##### Example Use Case: Change API Key or manipulate other paramters ```php <?php namespace Vendor\Extension\EventListener; use SGalinski\SgYoutube\Event\BeforeVimeoCallEvent; class BeforeYoutubeCallEventListener { public function __invoke(BeforeVimeoCallEvent $event): void { // Change the API key $event->setApiKey('your-new-api-key'); // Extend the max results limit by 10 videos $event->setMaxResultsWithFilters($event->getMaxResultsWithFilters() + 10); } } ``` #### 2. AfterYoutubeCallEvent **Description**: Add Custom Data to JSON Array ##### Example Use Case: Change API Key or manipulate other paramters ```php <?php namespace SGalinski\SgYoutube\EventListeners; use SGalinski\SgYoutube\Event\AfterVimeoCallEvent; class AfterYoutubeCallEventListener { public function __invoke(AfterVimeoCallEvent $event): void { $jsonArray = $event->getJsonArray(); // Add custom data $jsonArray['customData'] = 'This is some custom data'; $event->setJsonArray($jsonArray); } } ``` #### 3. AfterFilterVideosEvent **Description**: This event is triggered after the videos have been filtered, allowing you to further manipulate the filtered results. ##### Example Use Case: Remove the 10 videos that we added initially with the first event ```php <?php namespace SGalinski\SgYoutube\EventListeners; use SGalinski\SgYoutube\Event\AfterFilterVideosEvent; class AfterFilterVideosEventListener { public function __invoke(AfterFilterVideosEvent $event): void { // Modify the jsonArray if needed $jsonArray = $event->getJsonArray(); // Add some custom processing here // For example let's remove the extra 10 videos that we added in the other event if (count($jsonArray['items']) > 10) { array_splice($jsonArray['items'], 0, 10); } $event->setJsonArray($jsonArray); } } ``` #### 4. AfterMapCustomThumbnailsEvent **Description**: This event is triggered after custom thumbnails have been mapped, allowing you to modify the JSON array one last time before rendering. ##### Example Use Case: Use a custom thumbnail for all videos in the list ```php <?php namespace SGalinski\SgYoutube\EventListeners; use SGalinski\SgYoutube\Event\AfterMapCustomThumbnailsEvent; class AfterMapCustomThumbnailsEventListener { public function __invoke(AfterMapCustomThumbnailsEvent $event): void { $jsonArray = $event->getJsonArray(); // Add a custom thumbnail URL foreach ($jsonArray['items'] as &$item) { $item['customThumbnail'] = 'https://example.com/custom-thumbnail.jpg'; } $event->setJsonArray($jsonArray); } } ``` To enable the events just register them in your Services.php as follows (you can use yaml instead if you prefer): ```php $services->set(BeforeYoutubeCallEventListener::class) ->tag('event.listener', ['event' => BeforeYoutubeCallEvent::class]); $services->set(AfterYoutubeCallEventListener::class) ->tag('event.listener', ['event' => AfterYoutubeCallEvent::class]); $services->set(AfterFilterVideosEventListener::class) ->tag('event.listener', ['event' => AfterFilterVideosEvent::class]); $services->set(AfterMapCustomThumbnailsEventListener::class) ->tag('event.listener', ['event' => AfterMapCustomThumbnailsEvent::class]); ``` ## **Setting Up the Frontend Filers** ### **Step 1: Adding the Plugin to a Page** 1. In the TYPO3 backend, create or edit the page where you want to display YouTube videos. 2. Add a new content element and select the YouTube plugin from the list. 3. Provide the necessary YouTube details in the plugin settings (like YouTube API key, video/channel/playlist ID, max results, etc.). ### **Step 2: Disable caching for the extension and Cache validation for your web page** 1. In the TYPO3 backend, go to Settings -> Extensions and open the sg_youtube settings. Tick the box that says 'uncached' and save. 2. This way the the extension will not cache the results and the the data will be loaded dynamically based on the filter settings. 3. Set `$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['enforceValidation'] = false` in your TYPO3 configuration, otherwise using the frontend filters may result in a 404 page in some instances. ### **Step 3: Configuring Filters via FlexForm** 1. In the plugin's FlexForm settings, you will see a section for "Filters." 2. Depending on your site configuration, filters will be available in a select field. You can select the filters you want to display (like search term, video duration, etc.). 3. Save your settings and publish the page. --- ## **3. Using Filters in the Frontend** Once filters are enabled, they will appear on the frontend in a form, allowing users to interact with the displayed videos dynamically. For example: - **Search Term**: Allows users to input a search term to filter videos based on keywords. - **Duration Filter**: Provides a dropdown to filter videos by length (short, medium, long, etc.). The filter values are sent to the YouTube API, and the video list updates based on the selected criteria. --- ## **4. TypoScript Configuration for Filters** To define custom filters, the plugin uses TypoScript. Below is an example configuration and an explanation of each section: ### **Example TypoScript Configuration** ```typoscript filters { searchTerm { label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.searchTerm partial = Filters/TextField filterClass = SGalinski\SgYoutube\Filter\QueryStringFilter defaultValues { # search = test } position = top } duration { label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.duration partial = Filters/Dropdown filterClass = SGalinski\SgYoutube\Filter\DurationFilter position = top options { 0 { label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.duration.0 value = 1 } 1 { label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.duration.1 value = 2 } } } } submitButton { position = top cssClass = btn btn-default label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.submitButton } ``` ### **Explanation of the Configuration** #### **`filters`** This TypoScript object defines all the available filters for the YouTube plugin. Each filter is a separate sub-object, like `searchTerm` and `duration` in the example. #### **Filter Fields** 1. **`searchTerm`** (Text Field) - **label**: The label for the filter form field. This can be translated using the TYPO3 language file (`locallang.xlf`). - **partial**: Specifies which partial file to use for rendering the filter. In this case, it points to a text field (`Filters/TextField`). - **filterClass**: This is the PHP class responsible for processing the filter and applying it to the YouTube API request. For the `searchTerm`, it's a `QueryStringFilter`. - **defaultValues**: You can specify default values for the filter (commented out in the example). - **position**: Defines where the filter should be displayed on the page. It can be `top` (above the video list) or `bottom` (below the video list). 2. **`duration`** (Dropdown) - **label**: The label for the filter form field. - **partial**: Points to a dropdown partial (`Filters/Dropdown`). - **filterClass**: This is the PHP class responsible for processing the filter. For `duration`, it's `DurationFilter`. - **options**: This section defines the options for the dropdown, each with a `label` (for the user to see) and a `value` (sent to the API). - **position**: Indicates where the filter should be displayed on the page. #### **`submitButton`** - Defines the configuration for the submit button: - **position**: Determines where the submit button is placed in the form (`top` in this case). It accepts `top`, `bottom` or `both` - **cssClass**: Adds CSS classes for styling the button. - **label**: Provides the text for the submit button (translatable using `locallang.xlf`). --- ## **5. Adding Custom Filters** You can easily extend the plugin by defining your own filters in TypoScript: 1. **Create a New Filter** Add a new filter object under `filters` in your TypoScript setup. For example, if you want to filter by video quality, your configuration might look like this: ```typoscript filters { quality { label = LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:filter.quality partial = Filters/Dropdown filterClass = SGalinski\SgYoutube\Filter\QualityFilter position = top options { 0 { label = HD value = hd } 1 { label = SD value = sd } } } } ``` 2. **Implement the Filter Logic** You will need to define the corresponding PHP `filterClass` that handles the logic for applying the filter to the YouTube API request. For example, the `QualityFilter` class would process the selected video quality and add the appropriate parameters to the API request. It must implement the FilterInterface interface. The modifyRequest method is called before sending the reuqest, while the modifyResponse method is called after receiving the response. --- ## **6. Working with Filter Positions** You can choose where filters are displayed (above or below the video list) by setting the `position` attribute in the TypoScript configuration. This allows you to create a flexible layout for filters: - Filters with `position = top` will be displayed before the video list. - Filters with `position = bottom` will be displayed after the video list. For example: ```typoscript filters { searchTerm { position = top } duration { position = bottom } } ``` --- ## **7. Debugging and Error Handling** If something goes wrong (e.g., videos aren't displayed, or the API request fails), here are a few things to check: - Make sure the YouTube API key is correctly configured. - Check the TypoScript configuration for errors (e.g., missing or incorrect filter definitions). - Look for error messages in the TYPO3 backend log for issues related to the plugin. --- ## **8. Conclusion** With this flexible filter system, you can easily customize the YouTube video list based on user input. By defining filters in TypoScript and adding them to the plugin's FlexForm, you provide an interactive and tailored experience for your site's visitors. If you need to add more filters, just extend the TypoScript configuration, create the necessary filter classes, and you're ready to go!