├── .coveragerc ├── .github └── workflows │ ├── create_pr_on_push.yml │ └── tests.yml ├── .gitignore ├── .mergify.yml ├── .travis.yml ├── CHANGES.txt ├── LICENSE ├── MANIFEST.in ├── Procfile ├── README.rst ├── cache.json ├── development.ini ├── eitbapi ├── __init__.py ├── cors.py ├── radio.py ├── static │ ├── favicon.ico │ └── theme.css ├── templates │ └── index.pt ├── tests │ ├── __init__.py │ ├── test_cors.py │ ├── test_radio.py │ ├── test_tv.py │ └── test_views.py ├── tv.py ├── utils.py └── views.py ├── production.ini ├── radio-cache.json ├── rebuild_cache.py ├── requirements.txt ├── run ├── runapp.py ├── runtime.txt ├── setup.cfg └── setup.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | include = 3 | eitbapi/* 4 | omit = 5 | */test* 6 | -------------------------------------------------------------------------------- /.github/workflows/create_pr_on_push.yml: -------------------------------------------------------------------------------- 1 | name: Create PR on push 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - master 7 | 8 | jobs: 9 | auto-pull-request: 10 | name: PullRequestAction 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: pull-request-action 17 | uses: peter-evans/create-pull-request@v3 18 | env: 19 | token: ${{ secrets.REPO_SCOPED_TOKEN }} 20 | base: master 21 | branch: create-pull-request 22 | branch-suffix: timestamp 23 | title: "This is an automated PR to update the cache" 24 | body: "This PR was created automatically" 25 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # Generated from: 2 | # https://github.com/zopefoundation/meta/tree/master/config/pure-python 3 | name: tests 4 | 5 | on: 6 | push: 7 | branches: [master] 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | config: 15 | - ["3.9", "py39"] 16 | 17 | runs-on: ubuntu-latest 18 | name: ${{ matrix.config[1] }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up Python 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.config[0] }} 25 | - name: Pip cache 26 | uses: actions/cache@v2 27 | with: 28 | path: ~/.cache/pip 29 | key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }} 30 | restore-keys: | 31 | ${{ runner.os }}-pip-${{ matrix.config[0] }}- 32 | ${{ runner.os }}-pip- 33 | - name: Install dependencies 34 | run: | 35 | python -m pip install --upgrade pip 36 | pip install setuptools==45.0.0 37 | pip install -r requirements.txt 38 | pip install -e "".[testing]"" 39 | pip install -e . 40 | - name: Test 41 | run: python setup.py test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # IPython Notebook 69 | .ipynb_checkpoints 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # celery beat schedule file 75 | celerybeat-schedule 76 | 77 | # dotenv 78 | .env 79 | 80 | # virtualenv 81 | venv/ 82 | ENV/ 83 | myvenv/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # VS Code 92 | .vscode/* -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge on approval 3 | conditions: 4 | - "author=erral" 5 | - "check-success=py37, tests / py37" 6 | actions: 7 | merge: 8 | method: merge 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.7 4 | 5 | env: 6 | - PYRAMID_VERSION=1.10 7 | # command to install dependencies 8 | install: 9 | - sed -ie "s#pyramid==1.10#pyramid>=$PYRAMID_VERSION,<$PYRAMID_VERSION.999999#" requirements.txt 10 | - pip install setuptools==45.0.0 11 | - pip install -r requirements.txt 12 | - pip install -e ".[testing]" 13 | - pip install -e . 14 | # command to run tests 15 | script: coverage run --source=eitbapi setup.py test 16 | after_success: 17 | - pip install coveralls 18 | - coveralls 19 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 2017-02-05 2 | ---------- 3 | 4 | - Fix url cleaning with ':' character [erral] 5 | 6 | 1.0 7 | --- 8 | 9 | - Initial implementation 10 | [erral] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt *.ini *.cfg *.rst 2 | recursive-include eitbapi *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml 3 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: ./run 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | eitbapi 2 | ======= 3 | 4 | A pyramid_ app to expose an API for `EITB Nahieran`_ (unofficial) 5 | 6 | .. image:: https://travis-ci.com/erral/eitbapi.svg?branch=master 7 | :target: https://travis-ci.com/erral/eitbapi 8 | 9 | .. image:: https://coveralls.io/repos/github/erral/eitbapi/badge.svg 10 | :target: https://coveralls.io/github/erral/eitbapi 11 | 12 | View API online 13 | =============== 14 | 15 | https://still-castle-99749.herokuapp.com/ 16 | 17 | 18 | Installation 19 | ============= 20 | 21 | To run and install localy, create a virtualenv first and then:: 22 | 23 | $ pip install -r requirements.txt 24 | $ pip install -e . 25 | $ pserve development.ini --reload 26 | 27 | 28 | Deployment on Heroku 29 | ==================== 30 | 31 | You can deploy this app on Heroku_. To do so, first create an account at 32 | Heroku_ and install `Heroku Toolbelt`_ and login for the first time from the 33 | console:: 34 | 35 | $ heroku login 36 | 37 | Now you are ready to create the application on Heroku: 38 | 39 | - Clone this repository:: 40 | 41 | $ git clone git@github.com:erral/eitbapi 42 | $ cd eitbapi 43 | 44 | - Create the heroku app:: 45 | 46 | $ heroku create 47 | 48 | - Deploy the code to Heroku:: 49 | 50 | $ git push heroku master 51 | 52 | - Open the web browser pointing to the application:: 53 | 54 | $ heroku open 55 | 56 | .. unicode:: U+1F64C U+1F64C U+1F64C U+1F64C 57 | 58 | 59 | 60 | .. _pyramid: http://docs.pylonsproject.org/projects/pyramid 61 | .. _`EITB Nahieran`: http://www.eitb.tv 62 | .. _Heroku: https://www.heroku.com 63 | .. _`Heroku Toolbelt`: https://toolbelt.heroku.com/ 64 | -------------------------------------------------------------------------------- /cache.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /development.ini: -------------------------------------------------------------------------------- 1 | ### 2 | # app configuration 3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/narr/environment.html 4 | ### 5 | 6 | [app:main] 7 | use = egg:eitbapi 8 | 9 | pyramid.reload_templates = true 10 | pyramid.debug_authorization = false 11 | pyramid.debug_notfound = false 12 | pyramid.debug_routematch = false 13 | pyramid.default_locale_name = en 14 | pyramid.includes = 15 | pyramid_debugtoolbar 16 | 17 | # By default, the toolbar only appears for clients from IP addresses 18 | # '127.0.0.1' and '::1'. 19 | # debugtoolbar.hosts = 127.0.0.1 ::1 20 | 21 | ### 22 | # wsgi server configuration 23 | ### 24 | 25 | [server:main] 26 | use = egg:waitress#main 27 | host = 0.0.0.0 28 | port = 6543 29 | 30 | ### 31 | # logging configuration 32 | # http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/narr/logging.html 33 | ### 34 | 35 | [loggers] 36 | keys = root, eitbapi 37 | 38 | [handlers] 39 | keys = console 40 | 41 | [formatters] 42 | keys = generic 43 | 44 | [logger_root] 45 | level = INFO 46 | handlers = console 47 | 48 | [logger_eitbapi] 49 | level = DEBUG 50 | handlers = 51 | qualname = eitbapi 52 | 53 | [handler_console] 54 | class = StreamHandler 55 | args = (sys.stderr,) 56 | level = NOTSET 57 | formatter = generic 58 | 59 | [formatter_generic] 60 | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s 61 | -------------------------------------------------------------------------------- /eitbapi/__init__.py: -------------------------------------------------------------------------------- 1 | from pyramid.config import Configurator 2 | from pyramid.renderers import JSON 3 | from .cors import cors_options_view 4 | from .cors import NO_PERMISSION_REQUIRED 5 | from .cors import add_cors_preflight_handler 6 | from .cors import CorsPreflightPredicate 7 | from .cors import add_cors_to_response 8 | 9 | 10 | def main(global_config, **settings): 11 | """This function returns a Pyramid WSGI application.""" 12 | config = Configurator(settings=settings) 13 | config.include("pyramid_chameleon") 14 | 15 | config.add_directive("add_cors_preflight_handler", add_cors_preflight_handler) 16 | config.add_route_predicate("cors_preflight", CorsPreflightPredicate) 17 | 18 | config.add_subscriber(add_cors_to_response, "pyramid.events.NewResponse") 19 | 20 | config.add_route( 21 | "cors-options-preflight", "/{catch_all:.*}", cors_preflight=True, 22 | ) 23 | config.add_view( 24 | cors_options_view, 25 | route_name="cors-options-preflight", 26 | permission=NO_PERMISSION_REQUIRED, 27 | ) 28 | 29 | config.add_renderer("prettyjson", JSON(indent=4, sort_keys=True)) 30 | config.add_static_view("static", "static", cache_max_age=3600) 31 | config.add_route("home", "/") 32 | config.add_route("programs", "/playlist") 33 | config.add_route("program-type-list", "/program-type-list") 34 | config.add_route("program-type-news", "/program-type-news") 35 | config.add_route("playlist", "/playlist/{playlist_id}") 36 | config.add_route("playlist-per-type", "/type-playlist/{playlist_id}") 37 | config.add_route("radio", "/radio") 38 | config.add_route("radio-program-type-list", "/radio-program-type-list") 39 | config.add_route("radio-playlist-per-type", "/type-rplaylist/{playlist_id}") 40 | config.add_route("radio-stations", "/radio-stations") 41 | config.add_route( 42 | "radio-station-program-list", "/radio-station-programs/{station_id}" 43 | ) 44 | config.add_route("radioplaylist", "/rplaylist/{playlist_id:[a-zA-Z0-9\.\-\/]+}") 45 | config.add_route("episode", "/episode/{episode_url:[a-zA-Z0-9\.\-\/]+}") 46 | config.add_route('last-broadcast-list', '/last-broadcast-list') 47 | config.scan() 48 | return config.make_wsgi_app() 49 | -------------------------------------------------------------------------------- /eitbapi/cors.py: -------------------------------------------------------------------------------- 1 | from pyramid.security import NO_PERMISSION_REQUIRED 2 | 3 | 4 | def includeme(config): 5 | config.add_directive("add_cors_preflight_handler", add_cors_preflight_handler) 6 | config.add_route_predicate("cors_preflight", CorsPreflightPredicate) 7 | 8 | config.add_subscriber(add_cors_to_response, "pyramid.events.NewResponse") 9 | 10 | 11 | class CorsPreflightPredicate(object): 12 | def __init__(self, val, config): 13 | self.val = val 14 | 15 | def text(self): 16 | return "cors_preflight = %s" % bool(self.val) 17 | 18 | phash = text 19 | 20 | def __call__(self, context, request): 21 | if not self.val: 22 | return False 23 | return ( 24 | request.method == "OPTIONS" 25 | and "Origin" in request.headers 26 | and "Access-Control-Request-Method" in request.headers 27 | ) 28 | 29 | 30 | def add_cors_preflight_handler(config): 31 | config.add_route( 32 | "cors-options-preflight", "/{catch_all:.*}", cors_preflight=True, 33 | ) 34 | config.add_view( 35 | cors_options_view, 36 | route_name="cors-options-preflight", 37 | permission=NO_PERMISSION_REQUIRED, 38 | ) 39 | 40 | 41 | def add_cors_to_response(event): 42 | request = event.request 43 | response = event.response 44 | if "Origin" in request.headers: 45 | response.headers[ 46 | "Access-Control-Expose-Headers" 47 | ] = "Content-Type,Date,Content-Length,Authorization,X-Request-ID" 48 | response.headers["Access-Control-Allow-Origin"] = request.headers["Origin"] 49 | response.headers["Access-Control-Allow-Credentials"] = "true" 50 | 51 | 52 | def cors_options_view(context, request): 53 | response = request.response 54 | if "Access-Control-Request-Headers" in request.headers: 55 | response.headers["Access-Control-Allow-Methods"] = "OPTIONS,HEAD,GET" 56 | response.headers[ 57 | "Access-Control-Allow-Headers" 58 | ] = "Content-Type,Accept,Accept-Language,Authorization,X-Request-ID" 59 | return response 60 | -------------------------------------------------------------------------------- /eitbapi/radio.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | from __future__ import unicode_literals 3 | from eitbapi.utils import get_radio_program_data 4 | from eitbapi.utils import get_radio_program_types 5 | from eitbapi.utils import get_radio_programs 6 | from eitbapi.utils import get_radio_program_data_per_type 7 | from eitbapi.utils import get_radio_program_data_per_station 8 | from eitbapi.utils import get_radio_stations 9 | from eitbapi.utils import safe_encode 10 | from pyramid.view import view_config 11 | 12 | 13 | @view_config(route_name="radio", renderer="prettyjson") 14 | def radio(request): 15 | """get all information about all the programs. 16 | How: scrap the website and look for the javascript links. 17 | """ 18 | menudata = get_radio_program_data() 19 | 20 | result = { 21 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 22 | "@id": request.route_url("programs"), 23 | "@type": "Radio", 24 | "parent": {}, 25 | } 26 | 27 | results = [] 28 | 29 | for item in menudata: 30 | pl_id = item.get("id") 31 | while pl_id.startswith("/"): 32 | pl_id = pl_id[1:] 33 | data = { 34 | "@id": request.route_url("radioplaylist", playlist_id=pl_id), 35 | "@type": "Radio Playlist", 36 | "title": safe_encode(item.get("title")), 37 | "description": "", 38 | } 39 | if data not in results: 40 | results.append(data) 41 | 42 | result["member"] = sorted(results, key=lambda x: x.get("title", "").lower()) 43 | 44 | return result 45 | 46 | 47 | @view_config(route_name="radio-program-type-list", renderer="prettyjson") 48 | def radio_program_type_list(request): 49 | result = { 50 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 51 | "@id": request.route_url("radio-program-type-list"), 52 | "@type": "RadioTypeList", 53 | "parent": request.route_url("home"), 54 | "member": [], 55 | } 56 | member = [] 57 | categorydict = get_radio_program_types() 58 | for categoryname, categoryvalues in categorydict.items(): 59 | item = { 60 | "@id": request.route_url( 61 | "radio-playlist-per-type", 62 | playlist_id=categoryvalues.get("submenu", {}).get("hash", ""), 63 | ), 64 | "@type": "Radio-Type-Playlist", 65 | "parent": request.route_url("radio-program-type-list"), 66 | "title": categoryname, 67 | } 68 | member.append(item) 69 | 70 | result["member"] = sorted(member, key=lambda x: x.get("title", "")) 71 | return result 72 | 73 | 74 | @view_config(route_name="radio-stations", renderer="prettyjson") 75 | def radio_stations(request): 76 | result = { 77 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 78 | "@id": request.route_url("radio-stations"), 79 | "@type": "RadioStationList", 80 | "parent": request.route_url("home"), 81 | "member": [], 82 | } 83 | member = [] 84 | categorydict = get_radio_stations() 85 | for categoryname, categoryvalues in categorydict.items(): 86 | item = { 87 | "@id": request.route_url( 88 | "radio-station-program-list", 89 | station_id=categoryvalues.get("submenu", {}).get("hash", ""), 90 | ), 91 | "@type": "Radio Station Program list", 92 | "parent": request.route_url("radio-program-type-list"), 93 | "title": categoryname, 94 | } 95 | member.append(item) 96 | 97 | result["member"] = sorted(member, key=lambda x: x.get("title", "")) 98 | return result 99 | 100 | 101 | @view_config(route_name="radioplaylist", renderer="prettyjson") 102 | def radioplaylist(request): 103 | """ get all the information about the given program. 104 | """ 105 | playlist_id = request.matchdict["playlist_id"] 106 | 107 | result = { 108 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 109 | "@id": request.route_url("radioplaylist", playlist_id=playlist_id), 110 | "@type": "Radio Playlist", 111 | "parent": request.route_url("radio"), 112 | } 113 | results = [] 114 | radio_programs = get_radio_programs(playlist_id) 115 | for radio_program in radio_programs: 116 | item = { 117 | "@id": "", 118 | "@type": "Radio Program", 119 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 120 | "title": radio_program.get("title", ""), 121 | "date": radio_program.get("date", ""), 122 | "duration": radio_program.get("duration", ""), 123 | "url": radio_program.get("url", ""), 124 | } 125 | results.append(item) 126 | 127 | result["member"] = sorted(results, key=lambda x: x.get("date", ""), reverse=True) 128 | return result 129 | 130 | 131 | @view_config(route_name="radio-playlist-per-type", renderer="prettyjson") 132 | def radio_programs_per_type(request): 133 | playlist_id = request.matchdict["playlist_id"] 134 | result = { 135 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 136 | "@id": request.route_url("radioplaylist", playlist_id=playlist_id), 137 | "@type": "Radio Program Type List", 138 | "parent": {}, 139 | } 140 | menudata = get_radio_program_data_per_type(playlist_id) 141 | results = [] 142 | for item in menudata: 143 | data = { 144 | "@id": request.route_url("radioplaylist", playlist_id=item.get("id")), 145 | "@type": "Radio Playlist", 146 | "title": safe_encode(item.get("title")), 147 | "description": "", 148 | } 149 | if data not in results: 150 | results.append(data) 151 | 152 | result["member"] = sorted(results, key=lambda x: x.get("title", "")) 153 | return result 154 | 155 | 156 | @view_config(route_name="radio-station-program-list", renderer="prettyjson") 157 | def radio_programs_per_station(request): 158 | station_id = request.matchdict["station_id"] 159 | result = { 160 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 161 | "@id": request.route_url("radio-station-program-list", station_id=station_id), 162 | "@type": "Radio Station Program List", 163 | "parent": {}, 164 | } 165 | menudata = get_radio_program_data_per_station(station_id) 166 | results = [] 167 | for item in menudata: 168 | data = { 169 | "@id": request.route_url("radioplaylist", playlist_id=item.get("id")), 170 | "@type": "Radio Playlist", 171 | "title": safe_encode(item.get("title")), 172 | "description": "", 173 | } 174 | if data not in results: 175 | results.append(data) 176 | 177 | result["member"] = sorted(results, key=lambda x: x.get("title", "")) 178 | return result 179 | -------------------------------------------------------------------------------- /eitbapi/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erral/eitbapi/66598208b26a694ca8707523d8624383af42ad3c/eitbapi/static/favicon.ico -------------------------------------------------------------------------------- /eitbapi/static/theme.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erral/eitbapi/66598208b26a694ca8707523d8624383af42ad3c/eitbapi/static/theme.css -------------------------------------------------------------------------------- /eitbapi/templates/index.pt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | EITB Nahieran API 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 |

EITB Nahieran APIa

32 |

33 | Webgune honek EITB Nahieran zerbitzuaren APIa eskaintzen du. 34 |

35 |

36 | Ikusi aurkezpena 37 |

38 |
39 |
40 |
41 |
42 |
43 | 51 |
52 |
53 | 88 |
89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /eitbapi/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erral/eitbapi/66598208b26a694ca8707523d8624383af42ad3c/eitbapi/tests/__init__.py -------------------------------------------------------------------------------- /eitbapi/tests/test_cors.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from webtest import TestApp 3 | 4 | 5 | class CORSTests(unittest.TestCase): 6 | def setUp(self): 7 | from eitbapi import main 8 | 9 | self.app = main({}) 10 | self.testapp = TestApp(self.app) 11 | 12 | def test_acces_control_allow_origing_is_present(self): 13 | headers = {"Origin": "*"} 14 | res = self.testapp.get("/program-type-news", status=200, headers=headers) 15 | acao = res.headers.get("Access-Control-Allow-Origin") 16 | self.assertTrue(acao) 17 | -------------------------------------------------------------------------------- /eitbapi/tests/test_radio.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | 5 | class RadioTests(unittest.TestCase): 6 | def setUp(self): 7 | from eitbapi import main 8 | 9 | app = main({}) 10 | from webtest import TestApp 11 | 12 | self.testapp = TestApp(app) 13 | 14 | def test_radio(self): 15 | res = self.testapp.get("/radio", status=200) 16 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 17 | 18 | def test_radio_playlist(self): 19 | res = self.testapp.get("/radio", status=200) 20 | members = json.loads(res.text).get("member", []) 21 | for member in members[:2]: 22 | # Workaround to remove localhost prefix from id url 23 | url = member.get("@id").replace("http://localhost", "") 24 | result = self.testapp.get(url, status=200) 25 | self.assertTrue( 26 | result.headers.get("Content-type").startswith("application/json") 27 | ) 28 | 29 | def test_radio_program_types_list(self): 30 | res = self.testapp.get("/radio-program-type-list", status=200) 31 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 32 | 33 | def test_radio_program_type_playlist(self): 34 | res = self.testapp.get("/radio-program-type-list", status=200) 35 | members = json.loads(res.text).get("member", []) 36 | for member in members[:2]: 37 | # Workaround to remove localhost prefix from id url 38 | url = member.get("@id").replace("http://localhost", "") 39 | result = self.testapp.get(url, status=200) 40 | self.assertTrue( 41 | result.headers.get("Content-type").startswith("application/json") 42 | ) 43 | 44 | def test_radio_station_list(self): 45 | res = self.testapp.get("/radio-stations", status=200) 46 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 47 | 48 | def test_radio_station_program_list(self): 49 | res = self.testapp.get("/radio-stations", status=200) 50 | members = json.loads(res.text).get("member", []) 51 | for member in members: 52 | # Workaround to remove localhost prefix from id url 53 | url = member.get("@id").replace("http://localhost", "") 54 | result = self.testapp.get(url, status=200) 55 | self.assertTrue( 56 | result.headers.get("Content-type").startswith("application/json") 57 | ) 58 | -------------------------------------------------------------------------------- /eitbapi/tests/test_tv.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | 5 | class TVTests(unittest.TestCase): 6 | def setUp(self): 7 | from eitbapi import main 8 | 9 | app = main({}) 10 | from webtest import TestApp 11 | 12 | self.testapp = TestApp(app) 13 | 14 | def test_programs(self): 15 | res = self.testapp.get("/playlist", status=200) 16 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 17 | 18 | def test_program_playlist(self): 19 | res = self.testapp.get("/playlist", status=200) 20 | members = json.loads(res.text).get("member", []) 21 | for member in members[:2]: 22 | # Workaround to remove localhost prefix from id url 23 | url = member.get("@id").replace("http://localhost", "") 24 | result = self.testapp.get(url, status=200) 25 | self.assertTrue( 26 | result.headers.get("Content-type").startswith("application/json") 27 | ) 28 | 29 | def test_program_playlist_episode(self): 30 | res = self.testapp.get("/playlist", status=200) 31 | members = json.loads(res.text).get("member", []) 32 | for member in members[:1]: 33 | # Workaround to remove localhost prefix from id url 34 | url = member.get("@id").replace("http://localhost", "") 35 | result = self.testapp.get(url, status=200) 36 | submembers = json.loads(result.text).get("member", []) 37 | for submember in submembers[:2]: 38 | # Workaround to remove localhost prefix from id url 39 | suburl = submember.get("@id").replace("http://localhost", "") 40 | subresult = self.testapp.get(suburl, status=200) 41 | self.assertTrue( 42 | subresult.headers.get("Content-type").startswith("application/json") 43 | ) 44 | 45 | def test_one_episode(self): 46 | res = self.testapp.get("/playlist", status=200) 47 | members = json.loads(res.text).get("member", []) 48 | for member in members[:1]: 49 | # Workaround to remove localhost prefix from id url 50 | url = member.get("@id").replace("http://localhost", "") 51 | result = self.testapp.get(url, status=200) 52 | submembers = json.loads(result.text).get("member", []) 53 | for submember in submembers[:1]: 54 | # Workaround to remove localhost prefix from id url 55 | suburl = submember.get("@id").replace("http://localhost", "") 56 | subresult = self.testapp.get(suburl, status=200) 57 | files = json.loads(subresult.text).get("member", []) 58 | for file in files: 59 | fileurl = file.get("@id").replace("http://localhost", "") 60 | fileresult = self.testapp.get(fileurl, status=200) 61 | self.assertTrue( 62 | fileresult.headers.get("Content-type").startswith( 63 | "application/json" 64 | ) 65 | ) 66 | 67 | def test_program_type_list(self): 68 | res = self.testapp.get("/program-type-list", status=200) 69 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 70 | 71 | def test_program_type_news(self): 72 | res = self.testapp.get("/program-type-news", status=200) 73 | self.assertTrue(res.headers.get("Content-type").startswith("application/json")) 74 | 75 | def test_program_list_per_type(self): 76 | res = self.testapp.get("/program-type-list", status=200) 77 | members = json.loads(res.text).get("member", []) 78 | for member in members: 79 | # Workaround to remove localhost prefix from id url 80 | url = member.get("@id").replace("http://localhost", "") 81 | result = self.testapp.get(url, status=200) 82 | self.assertTrue( 83 | result.headers.get("Content-type").startswith("application/json") 84 | ) 85 | -------------------------------------------------------------------------------- /eitbapi/tests/test_views.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | class FunctionalTests(unittest.TestCase): 5 | def setUp(self): 6 | from eitbapi import main 7 | 8 | app = main({}) 9 | from webtest import TestApp 10 | 11 | self.testapp = TestApp(app) 12 | 13 | def test_root(self): 14 | res = self.testapp.get("/", status=200) 15 | self.assertTrue(b"Pyramid" not in res.body) 16 | -------------------------------------------------------------------------------- /eitbapi/tv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | from __future__ import unicode_literals 3 | from eitbapi.utils import create_internal_video_url 4 | from eitbapi.utils import EITB_PLAYLIST_BASE_URL 5 | from eitbapi.utils import EITB_VIDEO_BASE_URL 6 | from eitbapi.utils import get_tv_news_programs 7 | from eitbapi.utils import get_tv_program_data 8 | from eitbapi.utils import get_tv_program_data_per_type 9 | from eitbapi.utils import get_tv_program_types 10 | from eitbapi.utils import safe_encode 11 | from eitbapi.utils import get_last_broadcast_data 12 | from eitbapi.utils import date_to_iso_format 13 | from pyramid.view import view_config 14 | 15 | import datetime 16 | import pytz 17 | import requests 18 | import youtube_dl 19 | 20 | 21 | def prepare_program_list(request, menudata): 22 | result = { 23 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 24 | "@id": request.route_url("programs"), 25 | "@type": "TV", 26 | "parent": {}, 27 | } 28 | 29 | results = [] 30 | 31 | for item in menudata: 32 | data = { 33 | "@id": request.route_url("playlist", playlist_id=item.get("id")), 34 | "@type": "Playlist", 35 | "title": safe_encode(item.get("title")), 36 | "description": "", 37 | } 38 | if data not in results: 39 | results.append(data) 40 | 41 | result["member"] = sorted(results, key=lambda x: x.get("title", "").lower()) 42 | 43 | return result 44 | 45 | 46 | @view_config(route_name="programs", renderer="prettyjson") 47 | def programs(request): 48 | """get all information about all the programs. 49 | How: scrap the website and look for the javascript links. 50 | """ 51 | menudata = get_tv_program_data() 52 | return prepare_program_list(request, menudata) 53 | 54 | 55 | @view_config(route_name="program-type-list", renderer="prettyjson") 56 | def program_type_list(request): 57 | result = { 58 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 59 | "@id": request.route_url("program-type-list"), 60 | "@type": "TypeList", 61 | "parent": request.route_url("home"), 62 | "member": [], 63 | } 64 | member = [] 65 | categorydict = get_tv_program_types() 66 | for categoryname, categoryvalues in categorydict.items(): 67 | item = { 68 | "@id": request.route_url( 69 | "playlist-per-type", 70 | playlist_id=categoryvalues.get("submenu", {}).get("hash", ""), 71 | ), 72 | "@type": "Type-Playlist", 73 | "parent": request.route_url("program-type-list"), 74 | "title": categoryname, 75 | } 76 | member.append(item) 77 | 78 | result["member"] = sorted(member, key=lambda x: x.get("title")) 79 | return result 80 | 81 | 82 | @view_config(route_name="program-type-news", renderer="prettyjson") 83 | def program_type_news(request): 84 | result = { 85 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 86 | "@id": request.route_url("program-type-list"), 87 | "@type": "TypeList", 88 | "parent": request.route_url("home"), 89 | "member": [], 90 | } 91 | member = [] 92 | categorydict = get_tv_news_programs() 93 | for categoryname, categoryvalues in categorydict.items(): 94 | item = { 95 | "@id": request.route_url( 96 | "playlist-per-type", 97 | playlist_id=categoryvalues.get("submenu", {}).get("hash", ""), 98 | ), 99 | "@type": "Type-Playlist", 100 | "parent": request.route_url("program-type-list"), 101 | "title": categoryname, 102 | } 103 | member.append(item) 104 | 105 | result["member"] = sorted(member, key=lambda x: x.get("title")) 106 | return result 107 | 108 | 109 | @view_config(route_name="playlist-per-type", renderer="prettyjson") 110 | def programs_per_type(request): 111 | playlist_id = request.matchdict["playlist_id"] 112 | menudata = get_tv_program_data_per_type(playlist_id) 113 | return prepare_program_list(request, menudata) 114 | 115 | 116 | @view_config(route_name="playlist", renderer="prettyjson") 117 | def playlist(request): 118 | """ get all the information about the given program. 119 | How: get the information from a pseudo-api 120 | """ 121 | playlist_id = request.matchdict["playlist_id"] 122 | 123 | result = { 124 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 125 | "@id": request.route_url("playlist", playlist_id=playlist_id), 126 | "@type": "Playlist", 127 | "parent": request.route_url("programs"), 128 | } 129 | 130 | playlist_url = EITB_PLAYLIST_BASE_URL.format(playlist_id) 131 | try: 132 | data = requests.get(playlist_url) 133 | playlist_data = data.json() 134 | except ValueError: 135 | return result 136 | 137 | web_medias = playlist_data.get("web_media") 138 | del playlist_data["web_media"] 139 | 140 | member = [] 141 | for web_media in web_medias: 142 | language = web_media.get("IDIOMA", "") 143 | pubdatestr = web_media.get("PUB_DATE", "") 144 | broadcastdatestr = web_media.get("BROADCST_DATE", "") 145 | dateformat = "%Y-%m-%d %H:%M:%S" 146 | 147 | tz = pytz.timezone("Europe/Madrid") 148 | try: 149 | pubdate = datetime.datetime.strptime(pubdatestr, dateformat) 150 | pubdate = tz.localize(pubdate) 151 | pubdateiso = pubdate.isoformat() 152 | except (TypeError, ValueError): 153 | pubdateiso = pubdatestr 154 | 155 | try: 156 | broadcastdate = datetime.datetime.strptime(broadcastdatestr, dateformat) 157 | broadcastdate = tz.localize(broadcastdate) 158 | broadcastdateiso = broadcastdate.isoformat() 159 | except (TypeError, ValueError): 160 | broadcastdateiso = broadcastdatestr 161 | 162 | item = { 163 | "@id": create_internal_video_url( 164 | playlist_data.get("name_playlist"), 165 | playlist_data.get("id_web_playlist"), 166 | web_media.get("NAME_ES"), 167 | web_media.get("ID_WEB_MEDIA"), 168 | request=request, 169 | ), 170 | "@type": "Episode", 171 | "title": web_media.get("NAME_{}".format(language)), 172 | "title_eu": web_media.get("NAME_EU"), 173 | "title_es": web_media.get("NAME_ES"), 174 | "description": web_media.get("SHORT_DESC_{}".format(language)), 175 | "description_eu": web_media.get("SHORT_DESC_EU", ""), 176 | "description_es": web_media.get("SHORT_DESC_ES", ""), 177 | "publication_date": pubdateiso, 178 | "broadcast_date": broadcastdateiso, 179 | "episode_image": web_media.get("STILL_URL", ""), 180 | "episode_image_thumbnail": web_media.get("THUMBNAIL_URL", ""), 181 | "language": language.lower(), 182 | } 183 | if item not in member: 184 | member.append(item) 185 | 186 | playlist_data["member"] = sorted( 187 | member, key=lambda x: x.get("broadcast_date", "") or "", reverse=True 188 | ) 189 | 190 | del playlist_data["id"] 191 | 192 | result.update(playlist_data) 193 | return result 194 | 195 | 196 | @view_config(route_name="episode", renderer="prettyjson") 197 | def episode(request): 198 | """ Get all the information and the video links from a given episode. 199 | How: use youtube-dl to get the information 200 | """ 201 | episode_url = request.matchdict["episode_url"] 202 | 203 | url = EITB_VIDEO_BASE_URL + episode_url 204 | try: 205 | playlist_title, playlist_id, video_title, video_id = episode_url.split("/") 206 | except ValueError: 207 | return {} 208 | 209 | result = { 210 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 211 | "@id": request.route_url("episode", episode_url=episode_url), 212 | "@type": "Episode", 213 | "parent": request.route_url("playlist", playlist_id=playlist_id), 214 | } 215 | 216 | try: 217 | ydl = youtube_dl.YoutubeDL({"outtmpl": "%(id)s%(ext)s"}) 218 | video_data = ydl.extract_info(url, download=False) 219 | except youtube_dl.DownloadError: 220 | return result 221 | 222 | result.update(video_data) 223 | return result 224 | 225 | @view_config(route_name='last-broadcast-list', renderer='prettyjson') 226 | def last_broadcast_list(request): 227 | number_of_items = request.params.get('items', 25) 228 | playlist_data = get_last_broadcast_data(number_of_items) 229 | web_media = playlist_data['web_media'] 230 | data = [] 231 | result = { 232 | "@context": "http://www.w3.org/ns/hydra/context.jsonld", 233 | "@id": request.route_url("last-broadcast-list"), 234 | "@type": "Last tv shows", 235 | "parent": {}, 236 | } 237 | for item in web_media: 238 | language = item.get("IDIOMA", "") 239 | data.append({ 240 | '@id': create_internal_video_url( 241 | # playlist_data.get('name_playlist'), 242 | item.get('NAME_{}'.format(language)), 243 | playlist_data.get('id_web_playlist'), 244 | item.get('NAME_ES'), 245 | item.get('ID_WEB_MEDIA'), 246 | request=request, 247 | ), 248 | '@type': 'Episode', 249 | 'title': item.get('NAME_{}'.format(language)), 250 | 'title_eu': item.get('NAME_EU'), 251 | 'title_es': item.get('NAME_ES'), 252 | 'description': item.get('SHORT_DESC_{}'.format(language)), 253 | 'description_eu': item.get('SHORT_DESC_EU', ''), 254 | 'description_es': item.get('SHORT_DESC_ES', ''), 255 | 'publication_date': date_to_iso_format(item.get('PUB_DATE', '')), 256 | 'broadcast_date': date_to_iso_format(item.get('BROADCST_DATE', '')), 257 | 'episode_image': item.get('STILL_URL', ''), 258 | 'episode_image_thumbnail': item.get('THUMBNAIL_URL', ''), 259 | 'language': language.lower() 260 | }) 261 | result['member'] = data 262 | return result -------------------------------------------------------------------------------- /eitbapi/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | from __future__ import unicode_literals 3 | from bs4 import BeautifulSoup 4 | 5 | import requests 6 | import sys 7 | import xml.etree.ElementTree as ET 8 | import os 9 | import json 10 | import datetime 11 | import pytz 12 | 13 | 14 | if sys.version_info >= (3, 0, 0): 15 | # for Python 3 16 | PYTHON3 = True 17 | else: 18 | PYTHON3 = False 19 | 20 | 21 | EITB_PLAYLIST_BASE_URL = ( 22 | "https://mam.eitb.eus/mam/REST/ServiceMultiweb/Playlist/MULTIWEBTV/{}" 23 | ) 24 | EITB_VIDEO_BASE_URL = "https://www.eitb.tv/es/video/" 25 | EITB_VIDEO_URL = "https://www.eitb.tv/es/video/{}/{}/{}/{}/" 26 | 27 | EITB_BASE_URL = "https://www.eitb.tv/" 28 | 29 | EITB_CACHED_TV_PROGRAM_LIST_XML_URL = ( 30 | "https://raw.githubusercontent.com/erral/eitbapi/master/cache.json" 31 | ) 32 | EITB_TV_PROGRAM_LIST_XML_URL = "https://www.eitb.tv/eu/menu/getMenu/tv/" 33 | 34 | 35 | EITB_CACHED_RADIO_PROGRAM_LIST_XML_URL = "https://raw.githubusercontent.com/erral/eitbapi/master/radio-cache.json" 36 | EITB_RADIO_PROGRAM_LIST_XML_URL = "https://www.eitb.tv/es/menu/getMenu/radio/" 37 | 38 | EITB_LAST_BROADCAST_URL = 'https://mam.eitb.eus/mam/REST/ServiceMultiweb/SmartPlaylistByDestination/MULTIWEBTV/12/BROADCST_DATE/DESC/{}/' 39 | 40 | def safe_unicode(value, encoding="utf-8"): 41 | if PYTHON3: 42 | if isinstance(value, bytes): 43 | return value 44 | elif isinstance(value, str): 45 | try: 46 | value = bytes(value, encoding) 47 | except (UnicodeDecodeError): 48 | value = value.decode("utf-8", "replace") 49 | return value 50 | else: 51 | if isinstance(value, unicode): 52 | return value 53 | elif isinstance(value, basestring): 54 | try: 55 | value = unicode(value, encoding) 56 | except (UnicodeDecodeError): 57 | value = value.decode("utf-8", "replace") 58 | return value 59 | 60 | 61 | def safe_encode(value, charset="utf-8"): 62 | if PYTHON3: 63 | return safe_unicode(value, charset).decode(charset) 64 | else: 65 | return safe_unicode(value, charset).encode(charset) 66 | 67 | 68 | def xml_to_dict(data): 69 | try: 70 | root = ET.fromstring(data) 71 | except ET.ParseError: 72 | root = [] 73 | d = {} 74 | for item in root: 75 | dd = {} 76 | for subitem in item: 77 | m = {} 78 | m["text"] = subitem.text 79 | m.update(subitem.attrib) 80 | dd[subitem.tag] = m 81 | 82 | d[dd["title"]["text"]] = dd 83 | 84 | return d 85 | 86 | 87 | def build_program_list_by_hash(menu_hash, mode="tv", first=False): 88 | if mode == "tv": 89 | results = get_tv_submenu_data(menu_hash, first=first) 90 | elif mode == "radio": 91 | results = get_radio_submenu_data(menu_hash, first=first) 92 | return results 93 | 94 | 95 | def get_tv_program_data(): 96 | res = requests.get(EITB_CACHED_TV_PROGRAM_LIST_XML_URL) 97 | try: 98 | result = res.json() 99 | except: 100 | result = [] 101 | return result 102 | 103 | 104 | def _get_tv_program_data(): 105 | menudata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL) 106 | menudict = xml_to_dict(menudata.content) 107 | menu_hash = ( 108 | menudict.get("programas_az", {}).get("submenu", {}).get("hash", "") 109 | ) # noqa 110 | return build_program_list_by_hash(menu_hash, mode="tv", first=True) 111 | 112 | 113 | def get_tv_program_data_per_type(menu_hash): 114 | return build_program_list_by_hash(menu_hash, mode="tv") 115 | 116 | 117 | def get_tv_program_types(): 118 | menudata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL) 119 | menudict = xml_to_dict(menudata.content) 120 | menu_hash = ( 121 | menudict.get("por_categorias", {}).get("submenu", {}).get("hash", "") 122 | ) # noqa 123 | categorydata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL + "/" + menu_hash) 124 | categorydict = xml_to_dict(categorydata.content) 125 | return categorydict 126 | 127 | 128 | def get_tv_news_programs(): 129 | menudata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL) 130 | menudict = xml_to_dict(menudata.content) 131 | menu_hash = ( 132 | menudict.get("informativos", {}).get("submenu", {}).get("hash", "") 133 | ) # noqa 134 | categorydata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL + "/" + menu_hash) 135 | categorydict = xml_to_dict(categorydata.content) 136 | return categorydict 137 | 138 | def get_last_broadcast_data(number_of_items): 139 | listdata = requests.get(EITB_LAST_BROADCAST_URL.format(number_of_items)) 140 | listjson = json.loads(listdata.content) 141 | return listjson 142 | 143 | 144 | def get_radio_program_data(): 145 | res = requests.get(EITB_CACHED_RADIO_PROGRAM_LIST_XML_URL) 146 | try: 147 | result = res.json() 148 | except: 149 | result = [] 150 | return result 151 | 152 | 153 | def _get_radio_program_data(): 154 | results = [] 155 | menudata = requests.get(EITB_RADIO_PROGRAM_LIST_XML_URL) 156 | menudict = xml_to_dict(menudata.content) 157 | menu_hash = ( 158 | menudict.get("programas_az", {}).get("submenu", {}).get("hash", None) 159 | ) # noqa 160 | results = get_radio_submenu_data(menu_hash, first=True) 161 | return results 162 | 163 | 164 | def get_radio_program_types(): 165 | menudata = requests.get(EITB_RADIO_PROGRAM_LIST_XML_URL) 166 | menudict = xml_to_dict(menudata.content) 167 | menu_hash = ( 168 | menudict.get("por_categorias", {}).get("submenu", {}).get("hash", "") 169 | ) # noqa 170 | categorydata = requests.get( 171 | EITB_RADIO_PROGRAM_LIST_XML_URL + "/" + menu_hash 172 | ) # noqa 173 | categorydict = xml_to_dict(categorydata.content) 174 | return categorydict 175 | 176 | 177 | def get_radio_stations(): 178 | menudata = requests.get(EITB_RADIO_PROGRAM_LIST_XML_URL) 179 | menudict = xml_to_dict(menudata.content) 180 | menu_hash = ( 181 | menudict.get("por_emisoras", {}).get("submenu", {}).get("hash", "") 182 | ) # noqa 183 | categorydata = requests.get( 184 | EITB_RADIO_PROGRAM_LIST_XML_URL + "/" + menu_hash 185 | ) # noqa 186 | categorydict = xml_to_dict(categorydata.content) 187 | return categorydict 188 | 189 | 190 | def get_tv_submenu_data(menu_hash, pretitle="", first=False): 191 | submenudata = requests.get(EITB_TV_PROGRAM_LIST_XML_URL + "/" + menu_hash) 192 | submenudict = xml_to_dict(submenudata.content) 193 | results = [] 194 | for item in submenudict.values(): 195 | subhash = item.get("submenu", {}).get("hash", None) 196 | if subhash: 197 | if first: 198 | results += get_tv_submenu_data(subhash) 199 | else: 200 | results += get_tv_submenu_data( 201 | subhash, pretitle=item.get("title").get("text") 202 | ) # noqa 203 | 204 | data = {} 205 | data["title"] = ( 206 | pretitle + " " + item.get("title", {}).get("text", "") 207 | ).strip() # noqa 208 | data["id"] = item.get("id", {}).get("text", "") 209 | if data["id"]: 210 | results.append(data) 211 | 212 | return results 213 | 214 | 215 | def get_radio_submenu_data(menu_hash, pretitle="", first=False): 216 | submenudata = requests.get( 217 | EITB_RADIO_PROGRAM_LIST_XML_URL + "/" + menu_hash 218 | ) # noqa 219 | submenudict = xml_to_dict(submenudata.content) 220 | results = [] 221 | for item in submenudict.values(): 222 | subhash = item.get("submenu", {}).get("hash", None) 223 | if subhash: 224 | if first: 225 | results += get_radio_submenu_data(subhash) 226 | else: 227 | results += get_radio_submenu_data( 228 | subhash, pretitle=item.get("title").get("text") 229 | ) # noqa 230 | 231 | data = {} 232 | data["title"] = ( 233 | pretitle + " " + item.get("title", {}).get("text", "") 234 | ).strip() # noqa 235 | data["id"] = item.get("id", {}).get("text", "") 236 | if data["id"]: 237 | results.append(data) 238 | 239 | return results 240 | 241 | 242 | def create_internal_video_url( 243 | playlist_title, playlist_id, video_title, video_id, request=None 244 | ): # noqa 245 | """create an internal url to identify an episode inside this API.""" 246 | playlist_title = clean_title(playlist_title) 247 | video_title = clean_title(playlist_title) 248 | 249 | internal_url = "{}/{}/{}/{}".format( 250 | playlist_title, playlist_id, video_id, video_title 251 | ) # noqa 252 | return request.route_url("episode", episode_url=internal_url) 253 | 254 | 255 | def clean_title(title): 256 | """slugify the titles using the method that EITB uses in 257 | the website: 258 | - url: http://www.eitb.tv/resources/js/comun/comun.js 259 | - method: string2url 260 | """ 261 | translation_map = { 262 | "À": "A", 263 | "Á": "A", 264 | "Â": "A", 265 | "Ã": "A", 266 | "Ä": "A", 267 | "Å": "A", 268 | "Æ": "E", 269 | "È": "E", 270 | "É": "E", 271 | "Ê": "E", 272 | "Ë": "E", 273 | "Ì": "I", 274 | "Í": "I", 275 | "Î": "I", 276 | "Ï": "I", 277 | "Ò": "O", 278 | "Ó": "O", 279 | "Ô": "O", 280 | "Ö": "O", 281 | "Ù": "U", 282 | "Ú": "U", 283 | "Û": "U", 284 | "Ü": "U", 285 | "Ñ": "N", 286 | "?": "", 287 | "¿": "", 288 | "!": "", 289 | "¡": "", 290 | ":": "", 291 | "_": "-", 292 | "º": "", 293 | "ª": "a", 294 | ",": "", 295 | ".": "", 296 | "(": "", 297 | ")": "", 298 | "@": "", 299 | " ": "-", 300 | "&": "", 301 | "#": "", 302 | } 303 | 304 | val = title.upper() 305 | for k, v in translation_map.items(): 306 | val = val.replace(k, v) 307 | return val.lower() 308 | 309 | 310 | def get_radio_programs(playlist_id): 311 | while playlist_id.startswith("/"): 312 | playlist_id = playlist_id[1:] 313 | 314 | results = [] 315 | data = requests.get(EITB_BASE_URL + playlist_id) 316 | soup = BeautifulSoup(data.text, "html.parser") 317 | for li in soup.find_all("li", class_="audio_uno"): 318 | title_p, date_p, download_p = li.find_all("p") 319 | title = title_p.find("a").get("original-title") 320 | date, duration = date_p.text.split() 321 | url = download_p.find("a").get("href") 322 | duration = duration.replace("(", "").replace(")", "") 323 | item = dict(title=title, date=date, url=url, duration=duration) 324 | results.append(item) 325 | return results 326 | 327 | 328 | def get_radio_program_data_per_type(playlist_id): 329 | return build_program_list_by_hash(playlist_id, mode="radio", first=True) 330 | 331 | 332 | def get_radio_program_data_per_station(station_id): 333 | return build_program_list_by_hash(station_id, mode="radio", first=False) 334 | 335 | 336 | # PARSE DATE TO ISO FORMAT 337 | def date_to_iso_format(date): 338 | dateformat = '%Y-%m-%d %H:%M:%S' 339 | tz = pytz.timezone('Europe/Madrid') 340 | try: 341 | date_to_format = datetime.datetime.strptime(date, dateformat) 342 | date_to_format = tz.localize(date_to_format) 343 | dateiso = date_to_format.isoformat() 344 | except (TypeError, ValueError): 345 | dateiso = date 346 | return dateiso 347 | -------------------------------------------------------------------------------- /eitbapi/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | from __future__ import unicode_literals 3 | from pyramid.view import view_config 4 | 5 | 6 | @view_config(route_name="home", renderer="templates/index.pt") 7 | def index(request): 8 | return {"project": "eitbapi"} 9 | -------------------------------------------------------------------------------- /production.ini: -------------------------------------------------------------------------------- 1 | ### 2 | # app configuration 3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/narr/environment.html 4 | ### 5 | 6 | [app:main] 7 | use = egg:eitbapi 8 | 9 | pyramid.reload_templates = false 10 | pyramid.debug_authorization = false 11 | pyramid.debug_notfound = false 12 | pyramid.debug_routematch = false 13 | pyramid.default_locale_name = en 14 | 15 | ### 16 | # wsgi server configuration 17 | ### 18 | 19 | [server:main] 20 | use = egg:waitress#main 21 | host = 0.0.0.0 22 | port = 6543 23 | 24 | ### 25 | # logging configuration 26 | # http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/narr/logging.html 27 | ### 28 | 29 | [loggers] 30 | keys = root, eitbapi 31 | 32 | [handlers] 33 | keys = console 34 | 35 | [formatters] 36 | keys = generic 37 | 38 | [logger_root] 39 | level = WARN 40 | handlers = console 41 | 42 | [logger_eitbapi] 43 | level = WARN 44 | handlers = 45 | qualname = eitbapi 46 | 47 | [handler_console] 48 | class = StreamHandler 49 | args = (sys.stderr,) 50 | level = NOTSET 51 | formatter = generic 52 | 53 | [formatter_generic] 54 | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s 55 | -------------------------------------------------------------------------------- /radio-cache.json: -------------------------------------------------------------------------------- 1 | [{"title": "Africa is calling 2019-2020", "id": "/es/radio/radio-vitoria/africa-is-calling/6647281/"}, {"title": "Africa is calling 2018-2019", "id": "/es/radio/radio-vitoria/africa-is-calling/5836018/"}, {"title": "Africa is calling 2017-2018", "id": "/es/radio/radio-vitoria/africa-is-calling/5061031/"}, {"title": "Aldapeko 2022-2023", "id": "/es/radio/radio-vitoria/aldapeko/8950037/"}, {"title": "Aldapeko 2021-2022", "id": "/es/radio/radio-vitoria/aldapeko/8242700/"}, {"title": "Aldapeko 2020-2021", "id": "/es/radio/radio-vitoria/aldapeko/7414967/"}, {"title": "Aldapeko 2019-2020", "id": "/es/radio/radio-vitoria/aldapeko/6647324/"}, {"title": "Aldapeko 2018-2019", "id": "/es/radio/radio-vitoria/aldapeko/5836064/"}, {"title": "Aldapeko 2017-2018", "id": "/es/radio/radio-vitoria/aldapeko/5044570/"}, {"title": "Aldapeko 2016-2017", "id": "/es/radio/radio-vitoria/aldapeko/4317098/"}, {"title": "Aldapeko 2015-2016", "id": "/es/radio/radio-vitoria/aldapeko/3756068/"}, {"title": "Aldapeko 2014-2015", "id": "/es/radio/radio-vitoria/aldapeko/2493894/"}, {"title": "Alguien te est\u00e1 escuchando 2020-2021", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/7414972/"}, {"title": "Alguien te est\u00e1 escuchando 2019-2020", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/6647331/"}, {"title": "Alguien te est\u00e1 escuchando 2018-2019", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/5836072/"}, {"title": "Alguien te est\u00e1 escuchando 2017-2018", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/5044373/"}, {"title": "Alguien te est\u00e1 escuchando 2016-2017", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/4320838/"}, {"title": "Alguien te est\u00e1 escuchando 2015-2016", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/3434542/"}, {"title": "Alguien te est\u00e1 escuchando 2014-2015", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/2494116/"}, {"title": "Alguien te est\u00e1 escuchando 2013-2014", "id": "/es/radio/radio-vitoria/alguien-te-esta-escuchando/1989662/"}, {"title": "Amarauna 2022-2023", "id": "/eu/irratia/euskadi-irratia/amarauna/8912832/"}, {"title": "Amarauna 2021-2022", "id": "/eu/irratia/euskadi-irratia/amarauna/8281537/"}, {"title": "Amarauna 2020-2021", "id": "/eu/irratia/euskadi-irratia/amarauna/7467830/"}, {"title": "Amarauna 2019-2020", "id": "/eu/irratia/euskadi-irratia/amarauna/6637208/"}, {"title": "Amarauna 2018-2019", "id": "/eu/irratia/euskadi-irratia/amarauna/5827011/"}, {"title": "Amarauna 2017-2018", "id": "/eu/irratia/euskadi-irratia/amarauna/5051945/"}, {"title": "Amarauna 2016-2017", "id": "/eu/irratia/euskadi-irratia/amarauna/4343280/"}, {"title": "Amarauna 2015-2016", "id": "/eu/irratia/euskadi-irratia/amarauna/3445644/"}, {"title": "Amarauna 2014-2015", "id": "/eu/irratia/euskadi-irratia/amarauna/2515396/"}, {"title": "Amarauna 2013-2014", "id": "/eu/irratia/euskadi-irratia/amarauna/1425838/"}, {"title": "Amarauna 2012-2013", "id": "/eu/irratia/euskadi-irratia/amarauna/911146/"}, {"title": "Apertura Flamenca 2021-2022", "id": "/es/radio/radio-vitoria/apertura-flamenca/8296760/"}, {"title": "Apertura Flamenca 2020-2021", "id": "/es/radio/radio-vitoria/apertura-flamenca/7414985/"}, {"title": "Apertura Flamenca 2019- 2020", "id": "/es/radio/radio-vitoria/apertura-flamenca/6647348/"}, {"title": "Apertura Flamenca 2018- 2019", "id": "/es/radio/radio-vitoria/apertura-flamenca/5836389/"}, {"title": "Apertura Flamenca 2017- 2018", "id": "/es/radio/radio-vitoria/apertura-flamenca/5096399/"}, {"title": "Aqu\u00ed Macondo 2022-2023", "id": "/es/radio/radio-euskadi/aqui-macondo/8908930/"}, {"title": "Aqu\u00ed Macondo 2021-2022", "id": "/es/radio/radio-euskadi/aqui-macondo/8173873/"}, {"title": "Aqu\u00ed Macondo 2020-2021", "id": "/es/radio/radio-euskadi/aqui-macondo/7459979/"}, {"title": "Aqu\u00ed Macondo 2019-2020", "id": "/es/radio/radio-euskadi/aqui-macondo/6629339/"}, {"title": "Aqu\u00ed Macondo 2018-2019", "id": "/es/radio/radio-euskadi/aqui-macondo/5769378/"}, {"title": "Aqu\u00ed Macondo 2017-2018", "id": "/es/radio/radio-euskadi/aqui-macondo/5050309/"}, {"title": "Aqu\u00ed Macondo 2016-2017", "id": "/es/radio/radio-euskadi/aqui-macondo/4344310/"}, {"title": "Aqu\u00ed Macondo 2015-2016", "id": "/es/radio/radio-euskadi/aqui-macondo/3446136/"}, {"title": "Aqu\u00ed Macondo 2014-2015", "id": "/es/radio/radio-euskadi/aqui-macondo/2514880/"}, {"title": "Aqu\u00ed Macondo 2013-2014", "id": "/es/radio/radio-euskadi/aqui-macondo/1410544/"}, {"title": "Aqu\u00ed Macondo 2012-2013", "id": "/es/radio/radio-euskadi/aqui-macondo/953211/"}, {"title": "Aqu\u00ed Par\u00eds 2017-2018", "id": "/es/radio/radio-vitoria/aqui-paris/5044199/"}, {"title": "Aqu\u00ed Par\u00eds 2015-2016", "id": "/es/radio/radio-vitoria/aqui-paris/3434480/"}, {"title": "Aqu\u00ed Par\u00eds 2014-2015", "id": "/es/radio/radio-vitoria/aqui-paris/2493972/"}, {"title": "Aqu\u00ed Par\u00eds 2013-2014", "id": "/es/radio/radio-vitoria/aqui-paris/1517716/"}, {"title": "Aqu\u00ed Par\u00eds 2012-2013", "id": "/es/radio/radio-vitoria/aqui-paris/910542/"}, {"title": "Araba Gaur (Mediod\u00eda) 2022-2023", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/8950035/"}, {"title": "Araba Gaur (Mediod\u00eda) 2021-2022", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/8296689/"}, {"title": "Araba Gaur (Mediod\u00eda) mediod\u00eda) (2020-2021", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/7414990/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2019-2020", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/6647376/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2018-2019", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/5836393/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2017-2018", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/5044262/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2016-2017", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/4331358/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2015-2016", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/3434406/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2014-2015", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/2493888/"}, {"title": "Araba Gaur (Mediod\u00eda) Mediod\u00eda) (2013-2014", "id": "/es/radio/radio-vitoria/araba-gaur-mediodia/1478990/"}, {"title": "Argi Ibili 2022-2023", "id": "/eu/irratia/euskadi-irratia/argi-ibili/8912834/"}, {"title": "Argi Ibili 2021-2022", "id": "/eu/irratia/euskadi-irratia/argi-ibili/8281539/"}, {"title": "Argi Ibili 2020-2021", "id": "/eu/irratia/euskadi-irratia/argi-ibili/7467836/"}, {"title": "Argi Ibili 2019-2020", "id": "/eu/irratia/euskadi-irratia/argi-ibili/6637214/"}, {"title": "Argi Ibili 2018-2019", "id": "/eu/irratia/euskadi-irratia/argi-ibili/5836277/"}, {"title": "Argi Ibili 2017-2018", "id": "/eu/irratia/euskadi-irratia/argi-ibili/5051952/"}, {"title": "Argi Ibili 2016-2017", "id": "/eu/irratia/euskadi-irratia/argi-ibili/4343194/"}, {"title": "Argi Ibili 2015-2016", "id": "/eu/irratia/euskadi-irratia/argi-ibili/3445990/"}, {"title": "Arnasbideak 2021-2022", "id": "/eu/irratia/euskadi-irratia/arnasbideak/8350819/"}, {"title": "Arnasbideak 2020-2021", "id": "/eu/irratia/euskadi-irratia/arnasbideak/7578846/"}, {"title": "Arnasbideak 2019-2020", "id": "/eu/irratia/euskadi-irratia/arnasbideak/6665040/"}, {"title": "Arnasbideak 2018-2019", "id": "/eu/irratia/euskadi-irratia/arnasbideak/6306332/"}, {"title": "Arratsean 2022-2023", "id": "/eu/irratia/euskadi-irratia/arratsean/8912839/"}, {"title": "Arratsean 2021-2022", "id": "/eu/irratia/euskadi-irratia/arratsean/8281541/"}, {"title": "Arratsean 2020-2021", "id": "/eu/irratia/euskadi-irratia/arratsean/7467841/"}, {"title": "Arratsean 2019-2020", "id": "/eu/irratia/euskadi-irratia/arratsean/6637176/"}, {"title": "Arratsean 2018-2019", "id": "/eu/irratia/euskadi-irratia/arratsean/5844317/"}, {"title": "Arratsean 2017-2018", "id": "/eu/irratia/euskadi-irratia/arratsean/5051966/"}, {"title": "Arratsean 2015-2016", "id": "/eu/irratia/euskadi-irratia/arratsean/3445638/"}, {"title": "Arratsean 2014-2015", "id": "/eu/irratia/euskadi-irratia/arratsean/2515384/"}, {"title": "Arratsean 2016-2017", "id": "/eu/irratia/euskadi-irratia/arratsean/5050348/"}, {"title": "Arratsean 2013-2014", "id": "/eu/irratia/euskadi-irratia/arratsean/1413078/"}, {"title": "Aventureros 2022-2023", "id": "/es/radio/radio-vitoria/aventureros/8950028/"}, {"title": "Aventureros 2021-2022", "id": "/es/radio/radio-vitoria/aventureros/8242713/"}, {"title": "Aventureros 2020-2021", "id": "/es/radio/radio-vitoria/aventureros/7415005/"}, {"title": "Aventureros 2019-2020", "id": "/es/radio/radio-vitoria/aventureros/6647412/"}, {"title": "Aventureros 2018-2019", "id": "/es/radio/radio-vitoria/aventureros/5836405/"}, {"title": "Aventureros 2017-2018", "id": "/es/radio/radio-vitoria/aventureros/5044350/"}, {"title": "Aventureros 2016-2017", "id": "/es/radio/radio-vitoria/aventureros/4331478/"}, {"title": "Aventureros 2015-2016", "id": "/es/radio/radio-vitoria/aventureros/3434498/"}, {"title": "Aventureros 2014-2015", "id": "/es/radio/radio-vitoria/aventureros/2494050/"}, {"title": "Aventureros 2013-2014", "id": "/es/radio/radio-vitoria/aventureros/1554440/"}, {"title": "Aventureros 2012-2013", "id": "/es/radio/radio-vitoria/aventureros/911019/"}, {"title": "B Aldea B Aldea 2022-2023", "id": "/eu/irratia/gaztea/b-aldea/8912935/"}, {"title": "B Aldea 2021-2022", "id": "/eu/irratia/gaztea/b-aldea/8281636/"}, {"title": "B Aldea 2020-2021", "id": "/eu/irratia/gaztea/b-aldea/7473310/"}, {"title": "B Aldea 2019-2020", "id": "/eu/irratia/gaztea/b-aldea/6637333/"}, {"title": "B Aldea 2018-2019", "id": "/eu/irratia/gaztea/b-aldea/5831816/"}, {"title": "B Aldea 2017-2018", "id": "/eu/irratia/gaztea/b-aldea/5036108/"}, {"title": "B Aldea 2016-2017", "id": "/eu/irratia/gaztea/b-aldea/4429718/"}, {"title": "B Aldea 2015-2016", "id": "/eu/irratia/gaztea/b-aldea/3565658/"}, {"title": "B Aldea 2014-2015", "id": "/eu/irratia/gaztea/b-aldea/1397084/"}, {"title": "B Aldea 2013-2014", "id": "/eu/irratia/gaztea/b-aldea/2519152/"}, {"title": "B Aldea 2012-2013", "id": "/eu/irratia/gaztea/b-aldea/1233270/"}, {"title": "Baipasa 2022-2023", "id": "/eu/irratia/euskadi-irratia/baipasa/8912841/"}, {"title": "Baipasa 2021-2022", "id": "/eu/irratia/euskadi-irratia/baipasa/8281543/"}, {"title": "Baipasa 2020-2021", "id": "/eu/irratia/euskadi-irratia/baipasa/7467808/"}, {"title": "Baipasa 2019-2020", "id": "/eu/irratia/euskadi-irratia/baipasa/6903042/"}, {"title": "Barrubiak 2019-2020", "id": "/eu/irratia/euskadi-irratia/barrubiak/6637216/"}, {"title": "Barrubiak 2018-2019", "id": "/eu/irratia/euskadi-irratia/barrubiak/5836475/"}, {"title": "Barrubiak 2016-2017", "id": "/eu/irratia/euskadi-irratia/barrubiak/5088763/"}, {"title": "Bereziak 2019-2020", "id": "/eu/irratia/euskadi-irratia/bereziak/6637198/"}, {"title": "Bereziak 2018-2019", "id": "/eu/irratia/euskadi-irratia/bereziak/5203230/"}, {"title": "Bereziak 2017-2018", "id": "/eu/irratia/euskadi-irratia/bereziak/5049924/"}, {"title": "Bereziak 2016-2017", "id": "/eu/irratia/euskadi-irratia/bereziak/4585989/"}, {"title": "Beste ni 2021-2022", "id": "/eu/irratia/euskadi-irratia/beste-ni/8281545/"}, {"title": "Beste ni 2020-2021", "id": "/eu/irratia/euskadi-irratia/beste-ni/8179452/"}, {"title": "BESTE 40 URTEAN", "id": "/eu/irratia/euskadi-irratia/beste-40-urtean/8912845/"}, {"title": "Bihotzeko Fonoteka 2019-2020", "id": "/eu/irratia/euskadi-irratia/bihotzeko-fonoteka/6637196/"}, {"title": "Bihotzeko Fonoteka 2018-2019", "id": "/eu/irratia/euskadi-irratia/bihotzeko-fonoteka/6503356/"}, {"title": "Bihotzeko Fonoteka 2017-2018", "id": "/eu/irratia/euskadi-irratia/bihotzeko-fonoteka/5721152/"}, {"title": "Bogart baila con lobos 2022-2023", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/8950022/"}, {"title": "Bogart baila con lobos 2021-2022", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/8296695/"}, {"title": "Bogart baila con lobos 2020-2021", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/7415007/"}, {"title": "Bogart baila con lobos 2019-2020", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/6647507/"}, {"title": "Bogart baila con lobos 2018-2019", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/5836441/"}, {"title": "Bogart baila con lobos 2017-2018", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/5044273/"}, {"title": "Bogart baila con lobos 2016-2017", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/4320926/"}, {"title": "Bogart baila con lobos 2015-2016", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/3434424/"}, {"title": "Bogart baila con lobos 2014-2015", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/2493918/"}, {"title": "Bogart baila con lobos 2013-2014", "id": "/es/radio/radio-vitoria/bogart-baila-con-lobos/1481010/"}, {"title": "Boom Shaka Laka 2022-2023", "id": "/eu/irratia/gaztea/boom-shaka-laka/8912937/"}, {"title": "Boom Shaka Laka 2021-2022", "id": "/eu/irratia/gaztea/boom-shaka-laka/8281640/"}, {"title": "Boom Shaka Laka 2020-2021", "id": "/eu/irratia/gaztea/boom-shaka-laka/7477427/"}, {"title": "Boom Shaka Laka 2019-2020", "id": "/eu/irratia/gaztea/boom-shaka-laka/6637335/"}, {"title": "Boom Shaka Laka 2018-2019", "id": "/eu/irratia/gaztea/boom-shaka-laka/5831826/"}, {"title": "Boom Shaka Laka 2017-2018", "id": "/eu/irratia/gaztea/boom-shaka-laka/5036133/"}, {"title": "Boom Shaka Laka 2016-2017", "id": "/eu/irratia/gaztea/boom-shaka-laka/4366046/"}, {"title": "Boom Shaka Laka 2015-2016", "id": "/eu/irratia/gaztea/boom-shaka-laka/3485422/"}, {"title": "Boom Shaka Laka 2014-2015", "id": "/eu/irratia/gaztea/boom-shaka-laka/2519278/"}, {"title": "Boom Shaka Laka 2013-2014", "id": "/eu/irratia/gaztea/boom-shaka-laka/1397152/"}, {"title": "Boom Shaka Laka 2012-2013", "id": "/eu/irratia/gaztea/boom-shaka-laka/910466/"}, {"title": "Boulevard 2022-2023", "id": "/es/radio/radio-euskadi/boulevard/8909083/"}, {"title": "Boulevard 2021-2022", "id": "/es/radio/radio-euskadi/boulevard/8173879/"}, {"title": "Boulevard 2020-2021", "id": "/es/radio/radio-euskadi/boulevard/7459982/"}, {"title": "Boulevard 2019-2020", "id": "/es/radio/radio-euskadi/boulevard/6629357/"}, {"title": "Boulevard 2018-2019", "id": "/es/radio/radio-euskadi/boulevard/5769383/"}, {"title": "Boulevard 2017-2018", "id": "/es/radio/radio-euskadi/boulevard/5049705/"}, {"title": "Boulevard 2015-2016", "id": "/es/radio/radio-euskadi/boulevard/3446178/"}, {"title": "Boulevard 2014-2015", "id": "/es/radio/radio-euskadi/boulevard/2514934/"}, {"title": "Boulevard 2013-2014", "id": "/es/radio/radio-euskadi/boulevard/1414800/"}, {"title": "Boulevard 2012-2013", "id": "/es/radio/radio-euskadi/boulevard/945130/"}, {"title": "Boulevard 2011\u00962012", "id": "/es/radio/radio-euskadi/boulevard/911134/"}, {"title": "Boulevard 2016-2017", "id": "/es/radio/radio-euskadi/boulevard/4344156/"}, {"title": "Boulevard informativo Navarra 2022-2023", "id": "/es/radio/radio-euskadi/boulevard-informativo-navarra/8908934/"}, {"title": "Boulevard informativo Navarra 2021-2022", "id": "/es/radio/radio-euskadi/boulevard-informativo-navarra/8218075/"}, {"title": "Boulevard informativo Navarra 2020-2021", "id": "/es/radio/radio-euskadi/boulevard-informativo-navarra/7459960/"}, {"title": "Boulevard informativo Navarra 2019-2020", "id": "/es/radio/radio-euskadi/boulevard-informativo-navarra/6659826/"}, {"title": "Boulevard magazine Boulevard magazine 2020-2021", "id": "/es/radio/radio-euskadi/boulevard-magazine/7459992/"}, {"title": "Boulevard magazine Boulevard magazine 2019-2020", "id": "/es/radio/radio-euskadi/boulevard-magazine/6629373/"}, {"title": "Boulevard magazine boulevard magazine 2018-2019", "id": "/es/radio/radio-euskadi/boulevard-magazine/5769396/"}, {"title": "Boulevard magazine 2017 - 2018", "id": "/es/radio/radio-euskadi/boulevard-magazine/5051954/"}, {"title": "Cinta abierta 2020-2021", "id": "/es/radio/radio-euskadi/cinta-abierta/7465723/"}, {"title": "Cinta abierta 2019-2020", "id": "/es/radio/radio-euskadi/cinta-abierta/6629381/"}, {"title": "Cinta abierta 2018-2019", "id": "/es/radio/radio-euskadi/cinta-abierta/5769398/"}, {"title": "Cinta abierta 2017-2018", "id": "/es/radio/radio-euskadi/cinta-abierta/5049684/"}, {"title": "Cinta abierta 2016-2017", "id": "/es/radio/radio-euskadi/cinta-abierta/4344244/"}, {"title": "Cinta abierta 2015-2016", "id": "/es/radio/radio-euskadi/cinta-abierta/3446228/"}, {"title": "Cinta abierta 2014-2015", "id": "/es/radio/radio-euskadi/cinta-abierta/2567864/"}, {"title": "Club de Jazz 2019-2020", "id": "/es/radio/radio-vitoria/club-de-jazz/6647525/"}, {"title": "Club de Jazz 2018-2019", "id": "/es/radio/radio-vitoria/club-de-jazz/5836704/"}, {"title": "Club de Jazz 2017-2018", "id": "/es/radio/radio-vitoria/club-de-jazz/5044487/"}, {"title": "Club de Jazz 2016-2017", "id": "/es/radio/radio-vitoria/club-de-jazz/4320672/"}, {"title": "Club de Jazz 2015-2016", "id": "/es/radio/radio-vitoria/club-de-jazz/3434590/"}, {"title": "Club de Jazz 2014-2015", "id": "/es/radio/radio-vitoria/club-de-jazz/2741488/"}, {"title": "Club de Jazz 2013-2014", "id": "/es/radio/radio-vitoria/club-de-jazz/2741468/"}, {"title": "Club de Jazz 2012-2013", "id": "/es/radio/radio-vitoria/club-de-jazz/2741444/"}, {"title": "Consumidores Consumidores 2022-2023", "id": "/es/radio/radio-vitoria/consumidores/8950015/"}, {"title": "Consumidores 2021-2022", "id": "/es/radio/radio-vitoria/consumidores/8242724/"}, {"title": "Consumidores 2020-2021", "id": "/es/radio/radio-vitoria/consumidores/7415022/"}, {"title": "Consumidores 2019-2020", "id": "/es/radio/radio-vitoria/consumidores/6647538/"}, {"title": "Consumidores 2018-2019", "id": "/es/radio/radio-vitoria/consumidores/5834751/"}, {"title": "Consumidores 2017-2018", "id": "/es/radio/radio-vitoria/consumidores/5044463/"}, {"title": "Consumidores 2016-2017", "id": "/es/radio/radio-vitoria/consumidores/4320692/"}, {"title": "Consumidores 2015-2016", "id": "/es/radio/radio-vitoria/consumidores/3434566/"}, {"title": "Consumidores 2014-2015", "id": "/es/radio/radio-vitoria/consumidores/2561052/"}, {"title": "Cronica de Euskadi 2022-2023", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/8909138/"}, {"title": "Cronica de Euskadi 2021-2022", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/8218085/"}, {"title": "Cronica de Euskadi 2020-2021", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/7465779/"}, {"title": "Cronica de Euskadi 2019-2020", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/6629399/"}, {"title": "Cronica de Euskadi 2018 - 2019", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/5769400/"}, {"title": "Cronica de Euskadi 2017 - 2018", "id": "/es/radio/radio-euskadi/cronica-de-euskadi/5102941/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2022-2023", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/8909143/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2021-2022", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/8218090/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana Cr\u00f3nica del fin de semana 2020-2021", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/7465796/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2019-2020", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/6630959/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2018-2019", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/5769402/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2017-2018", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/5049681/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2015-2016", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/3446222/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2014-2015", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/2514964/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2013-2014", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/1838236/"}, {"title": "Cr\u00f3nica de Euskadi - Fin de semana 2016-2017", "id": "/es/radio/radio-euskadi/cronica-de-euskadi-fin-de-semana/4344256/"}, {"title": "Cronopios y famas 2022-2023", "id": "/es/radio/radio-vitoria/cronopios-y-famas/8950011/"}, {"title": "Cronopios y famas 2021-2022", "id": "/es/radio/radio-vitoria/cronopios-y-famas/8236177/"}, {"title": "Cronopios y famas 2020-2021", "id": "/es/radio/radio-vitoria/cronopios-y-famas/7305014/"}, {"title": "Cronopios y famas 2019-2020", "id": "/es/radio/radio-vitoria/cronopios-y-famas/6647717/"}, {"title": "Cronopios y famas 2018-2019", "id": "/es/radio/radio-vitoria/cronopios-y-famas/5837001/"}, {"title": "Cronopios y famas 2017-2018", "id": "/es/radio/radio-vitoria/cronopios-y-famas/5044543/"}, {"title": "Cronopios y famas 2016-2017", "id": "/es/radio/radio-vitoria/cronopios-y-famas/4320646/"}, {"title": "Cronopios y famas 2015-2016", "id": "/es/radio/radio-vitoria/cronopios-y-famas/3477176/"}, {"title": "Cursos de verano Cursos de verano 2022", "id": "/es/radio/radio-euskadi/cursos-de-verano/8895927/"}, {"title": "Cursos de verano Cursos de verano 2021", "id": "/es/radio/radio-euskadi/cursos-de-verano/8147013/"}, {"title": "Cursos de verano Cursos de verano UPV-EHU 2020", "id": "/es/radio/radio-euskadi/cursos-de-verano/7340544/"}, {"title": "Cursos de verano Cursos de verano 2019", "id": "/es/radio/radio-euskadi/cursos-de-verano/6526344/"}, {"title": "Cursos de verano Cursos de verano 2018", "id": "/es/radio/radio-euskadi/cursos-de-verano/5694439/"}, {"title": "D\u00e9jate llevar D\u00e9jate llevar 2022-2023", "id": "/es/radio/radio-vitoria/dejate-llevar/8949998/"}, {"title": "D\u00e9jate llevar 2021-2022", "id": "/es/radio/radio-vitoria/dejate-llevar/8242698/"}, {"title": "D\u00e9jate llevar 2020-2021", "id": "/es/radio/radio-vitoria/dejate-llevar/7738229/"}, {"title": "Di\u00e1logos", "id": "/es/radio/radio-vitoria/dialogos/8952750/"}, {"title": "Dida! Dida! 2022-2023", "id": "/eu/irratia/gaztea/dida/8912942/"}, {"title": "Dida! 2021-2022", "id": "/eu/irratia/gaztea/dida/8281642/"}, {"title": "Dida! 2020-2021", "id": "/eu/irratia/gaztea/dida/7473317/"}, {"title": "Dida! 2019-2020", "id": "/eu/irratia/gaztea/dida/6637340/"}, {"title": "Dida! 2018-2019", "id": "/eu/irratia/gaztea/dida/5841648/"}, {"title": "Dida! 2017-2018", "id": "/eu/irratia/gaztea/dida/5036142/"}, {"title": "Dida! 2016-2017", "id": "/eu/irratia/gaztea/dida/4357376/"}, {"title": "Dida! 2015-2016", "id": "/eu/irratia/gaztea/dida/3474310/"}, {"title": "Dida! 2014-2015", "id": "/eu/irratia/gaztea/dida/2519188/"}, {"title": "Dida! 2013-2014", "id": "/eu/irratia/gaztea/dida/1449566/"}, {"title": "Dida! 2012-2013", "id": "/eu/irratia/gaztea/dida/910442/"}, {"title": "Dida! Laburpena Dida! Labur 2022-2023", "id": "/eu/irratia/gaztea/dida-laburpena/8912949/"}, {"title": "Dida! Laburpena 2021-2022", "id": "/eu/irratia/gaztea/dida-laburpena/8281646/"}, {"title": "Dida! Laburpena 2020-2021", "id": "/eu/irratia/gaztea/dida-laburpena/7473385/"}, {"title": "Dida! Laburpena 2019-2020", "id": "/eu/irratia/gaztea/dida-laburpena/6637352/"}, {"title": "Dida! Laburpena 2018-2019", "id": "/eu/irratia/gaztea/dida-laburpena/5846985/"}, {"title": "Dida! Laburpena 2017-2018", "id": "/eu/irratia/gaztea/dida-laburpena/5057414/"}, {"title": "Dida! Laburpena 2016-2017", "id": "/eu/irratia/gaztea/dida-laburpena/4357444/"}, {"title": "Dida! Laburpena 2015-2016", "id": "/eu/irratia/gaztea/dida-laburpena/3474292/"}, {"title": "Dida! Laburpena 2014-2015", "id": "/eu/irratia/gaztea/dida-laburpena/2547176/"}, {"title": "Dida! Laburpena 2013-2014", "id": "/eu/irratia/gaztea/dida-laburpena/1397162/"}, {"title": "Dida! Laburpena 2012-2013", "id": "/eu/irratia/gaztea/dida-laburpena/910438/"}, {"title": "Distrito Euskadi 2022-2023", "id": "/es/radio/radio-euskadi/distrito-euskadi/8909175/"}, {"title": "Distrito Euskadi 2021-2022", "id": "/es/radio/radio-euskadi/distrito-euskadi/8175350/"}, {"title": "EiTB Musikako dokumentalak 2016", "id": "/eu/irratia/eitb-musika/eitb-musikako-dokumentalak/3866986/"}, {"title": "EiTB Musikako dokumentalak 2017", "id": "/eu/irratia/eitb-musika/eitb-musikako-dokumentalak/4587800/"}, {"title": "EiTB Musikako dokumentalak 2015", "id": "/eu/irratia/eitb-musika/eitb-musikako-dokumentalak/2989012/"}, {"title": "EiTB Musikako dokumentalak 2014", "id": "/eu/irratia/eitb-musika/eitb-musikako-dokumentalak/1888752/"}, {"title": "EiTB Musikako dokumentalak 2013", "id": "/eu/irratia/eitb-musika/eitb-musikako-dokumentalak/1233452/"}, {"title": "Ekosfera 2022-2023", "id": "/eu/irratia/euskadi-irratia/ekosfera/8912850/"}, {"title": "Ekosfera 2021-2022", "id": "/eu/irratia/euskadi-irratia/ekosfera/8281569/"}, {"title": "Ekosfera 2020-2021", "id": "/eu/irratia/euskadi-irratia/ekosfera/7467911/"}, {"title": "Ekosfera 2019-2020", "id": "/eu/irratia/euskadi-irratia/ekosfera/6637240/"}, {"title": "Ekosfera 2018-2019", "id": "/eu/irratia/euskadi-irratia/ekosfera/5827146/"}, {"title": "Ekosfera 2017-2018", "id": "/eu/irratia/euskadi-irratia/ekosfera/5049962/"}, {"title": "Ekosfera 2016-2017", "id": "/eu/irratia/euskadi-irratia/ekosfera/4343352/"}, {"title": "Ekosfera 2015-2016", "id": "/eu/irratia/euskadi-irratia/ekosfera/3445534/"}, {"title": "Ekosfera 2014-2015", "id": "/eu/irratia/euskadi-irratia/ekosfera/2515264/"}, {"title": "Ekosfera 2013-2014", "id": "/eu/irratia/euskadi-irratia/ekosfera/1382680/"}, {"title": "Ekosfera 2012-2013", "id": "/eu/irratia/euskadi-irratia/ekosfera/911173/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2020-2021", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/8164410/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2018-2019", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/5961942/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2017-2018", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/5049927/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2016-2017", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/4343152/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2015-2016", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/3697086/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2014-2015", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/3697068/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2012-2013", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/911232/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2010-2011", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/965664/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2021-2022", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/8281576/"}, {"title": "EKT | Euskal Kulturgintzaren Transmisioa 2019-2020", "id": "/eu/irratia/euskadi-irratia/ekt-euskal-kulturgintzaren-transmisioa/6637242/"}, {"title": "El jazz 2019-2020", "id": "/es/radio/radio-vitoria/el-jazz/6647749/"}, {"title": "El jazz 2018-2019", "id": "/es/radio/radio-vitoria/el-jazz/5837006/"}, {"title": "El jazz 2017-2018", "id": "/es/radio/radio-vitoria/el-jazz/5044367/"}, {"title": "El jazz 2016-2017", "id": "/es/radio/radio-vitoria/el-jazz/4320844/"}, {"title": "El jazz 2015-2016", "id": "/es/radio/radio-vitoria/el-jazz/3434516/"}, {"title": "El jazz 2014-2015", "id": "/es/radio/radio-vitoria/el-jazz/2494068/"}, {"title": "El jazz 2013-2014", "id": "/es/radio/radio-vitoria/el-jazz/1773578/"}, {"title": "El jazz 2012-2013", "id": "/es/radio/radio-vitoria/el-jazz/910533/"}, {"title": "El Ladrillo 2021\u00a02022", "id": "/es/radio/radio-vitoria/el-ladrillo/8242613/"}, {"title": "El Ladrillo 2020-2021", "id": "/es/radio/radio-vitoria/el-ladrillo/7415034/"}, {"title": "El Ladrillo 2019-2020", "id": "/es/radio/radio-vitoria/el-ladrillo/6647762/"}, {"title": "El Ladrillo 2018-2019", "id": "/es/radio/radio-vitoria/el-ladrillo/5837010/"}, {"title": "El Ladrillo 2017-2018", "id": "/es/radio/radio-vitoria/el-ladrillo/5044164/"}, {"title": "El Ladrillo 2016-2017", "id": "/es/radio/radio-vitoria/el-ladrillo/4811713/"}, {"title": "El local de la Jungla", "id": "/es/radio/radio-euskadi/el-local-de-la-jungla/7705093/"}, {"title": "El Mirador 2016-2017", "id": "/es/radio/radio-vitoria/el-mirador/4331364/"}, {"title": "El Mirador 2015-2016", "id": "/es/radio/radio-vitoria/el-mirador/3434400/"}, {"title": "El Mirador 2014-2015", "id": "/es/radio/radio-vitoria/el-mirador/2493852/"}, {"title": "El Mirador 2013-2014", "id": "/es/radio/radio-vitoria/el-mirador/1467318/"}, {"title": "El \u00faltimo apuntador El \u00faltimo apuntador 2022-2023", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/8949994/"}, {"title": "El \u00faltimo apuntador El \u00faltimo apuntador 2021-2022", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/8242617/"}, {"title": "El \u00faltimo apuntador 2020-2021", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/7415036/"}, {"title": "El \u00faltimo apuntador 2019-2020", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/6647766/"}, {"title": "El \u00faltimo apuntador 2018-2019", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/5834781/"}, {"title": "El \u00faltimo apuntador 2017-2018", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/5044466/"}, {"title": "El \u00faltimo apuntador 2016-2017", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/4320678/"}, {"title": "El \u00faltimo apuntador 2015-2016", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/3434584/"}, {"title": "El \u00faltimo apuntador 2014-2015", "id": "/es/radio/radio-vitoria/el-ultimo-apuntador/2604820/"}, {"title": "Entre Calles 2022", "id": "/es/radio/radio-euskadi/entre-calles/8928560/"}, {"title": "Entre Calles 2021", "id": "/es/radio/radio-euskadi/entre-calles/8147011/"}, {"title": "Entre Calles Entre Calles 2020", "id": "/es/radio/radio-euskadi/entre-calles/7340546/"}, {"title": "Entre Calles 2019", "id": "/es/radio/radio-euskadi/entre-calles/6562823/"}, {"title": "Entre Calles Entre Calles 2018", "id": "/es/radio/radio-euskadi/entre-calles/5694437/"}, {"title": "Euskal Musikarik Onena Castellano Euskal musikarik onena C 2021-2022", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/8242628/"}, {"title": "Euskal Musikarik Onena Castellano 2020-2021", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/7415110/"}, {"title": "Euskal Musikarik Onena Castellano 2019-2020", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/6647768/"}, {"title": "Euskal Musikarik Onena Castellano 2018-2019", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/5837065/"}, {"title": "Euskal Musikarik Onena Castellano 2017-2018", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/5061097/"}, {"title": "Euskal Musikarik Onena Castellano 2016-2017", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/4320658/"}, {"title": "Euskal Musikarik Onena Castellano 2015-2016", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/3434596/"}, {"title": "Euskal Musikarik Onena Castellano 2014-2015", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-castellano/3263762/"}, {"title": "Euskal Musikarik Onena Euskera 2022-2023", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-euskera/8949992/"}, {"title": "Euskal Musikarik Onena Euskera Euskal musikarik onena E 2021-2022", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-euskera/8236250/"}, {"title": "Euskal Musikarik Onena Euskera 2020-2021", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-euskera/7415038/"}, {"title": "Euskal Musikarik Onena Euskera 2019-2020", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-euskera/6647782/"}, {"title": "Euskal Musikarik Onena Euskera 2018-2019", "id": "/es/radio/radio-vitoria/euskal-musikarik-onena-euskera/5837070/"}, {"title": "Extra\u00f1os en un tren 2019-2020", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/6647788/"}, {"title": "Extra\u00f1os en un tren 2018-2019", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/5837072/"}, {"title": "Extra\u00f1os en un tren 2017-2018", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/5044582/"}, {"title": "Extra\u00f1os en un tren 2016-2017", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/4581979/"}, {"title": "Extra\u00f1os en un tren 2015-2016", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/4235898/"}, {"title": "Extra\u00f1os en un tren 2014-2015", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/3344280/"}, {"title": "Extra\u00f1os en un tren 2013-2014", "id": "/es/radio/radio-vitoria/extranos-en-un-tren/2375708/"}, {"title": "Faktoria 2022-2023", "id": "/eu/irratia/euskadi-irratia/faktoria/8912852/"}, {"title": "Faktoria 2021-2022", "id": "/eu/irratia/euskadi-irratia/faktoria/8281578/"}, {"title": "Faktoria 2020-2021", "id": "/eu/irratia/euskadi-irratia/faktoria/7467744/"}, {"title": "Faktoria 2019-2020", "id": "/eu/irratia/euskadi-irratia/faktoria/6637244/"}, {"title": "Faktoria 2018-2019", "id": "/eu/irratia/euskadi-irratia/faktoria/5834125/"}, {"title": "Faktoria 2017-2018", "id": "/eu/irratia/euskadi-irratia/faktoria/5049959/"}, {"title": "Faktoria 2016-2017", "id": "/eu/irratia/euskadi-irratia/faktoria/4343250/"}, {"title": "Faktoria 2015-2016", "id": "/eu/irratia/euskadi-irratia/faktoria/3445668/"}, {"title": "Faktoria 2014-2015", "id": "/eu/irratia/euskadi-irratia/faktoria/2518180/"}, {"title": "Faktoria 2013-2014", "id": "/eu/irratia/euskadi-irratia/faktoria/1435286/"}, {"title": "Faktoria 2012-2013", "id": "/eu/irratia/euskadi-irratia/faktoria/911144/"}, {"title": "Faktoria magazina 2022-2023", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/8912854/"}, {"title": "Faktoria magazina 2021-2022", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/8281584/"}, {"title": "Faktoria magazina 2020-2021", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/7467763/"}, {"title": "Faktoria magazina 2019-2020", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/6637246/"}, {"title": "Faktoria magazina 2018-2019", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/5089437/"}, {"title": "Faktoria magazina 2017-2018", "id": "/eu/irratia/euskadi-irratia/faktoria-magazina/5052601/"}, {"title": "Flaca Dj Flaca DJ 2022-2023", "id": "/eu/irratia/gaztea/flaca-dj/8912951/"}, {"title": "Flaca Dj Flaca DJ 2021-2022", "id": "/eu/irratia/gaztea/flaca-dj/8281648/"}, {"title": "Flaca Dj Flaca DJ 2020-2021", "id": "/eu/irratia/gaztea/flaca-dj/7944621/"}, {"title": "Fuera de juego 2021-2022", "id": "/es/radio/radio-euskadi/fuera-de-juego/8218116/"}, {"title": "Fuera de juego 2020-2021", "id": "/es/radio/radio-euskadi/fuera-de-juego/7465809/"}, {"title": "Fuera de juego 2019-2020", "id": "/es/radio/radio-euskadi/fuera-de-juego/6629408/"}, {"title": "Fuera de juego 2018-2019", "id": "/es/radio/radio-euskadi/fuera-de-juego/5769415/"}, {"title": "Fuera de juego 2017-2018", "id": "/es/radio/radio-euskadi/fuera-de-juego/5049634/"}, {"title": "Fuera de juego 2016-2017", "id": "/es/radio/radio-euskadi/fuera-de-juego/4344324/"}, {"title": "Fuera de juego 2015-2016", "id": "/es/radio/radio-euskadi/fuera-de-juego/3446016/"}, {"title": "Fuera de juego 2014-2015", "id": "/es/radio/radio-euskadi/fuera-de-juego/2514808/"}, {"title": "Fuera de juego 2013-2014", "id": "/es/radio/radio-euskadi/fuera-de-juego/1411288/"}, {"title": "Fuera de juego 2012-2013", "id": "/es/radio/radio-euskadi/fuera-de-juego/945020/"}, {"title": "Fuera de juego 2011-2012", "id": "/es/radio/radio-euskadi/fuera-de-juego/912610/"}, {"title": "Ganbara 2022-2023", "id": "/es/radio/radio-euskadi/ganbara/8909146/"}, {"title": "Ganbara 2021-2022", "id": "/es/radio/radio-euskadi/ganbara/8218151/"}, {"title": "Ganbara Ganbara 2020-2021", "id": "/es/radio/radio-euskadi/ganbara/7465814/"}, {"title": "Ganbara 2019-2020", "id": "/es/radio/radio-euskadi/ganbara/6629474/"}, {"title": "Ganbara 2018-2019", "id": "/es/radio/radio-euskadi/ganbara/5769417/"}, {"title": "Ganbara 2017-2018", "id": "/es/radio/radio-euskadi/ganbara/5049666/"}, {"title": "Ganbara 2016-2017", "id": "/es/radio/radio-euskadi/ganbara/4344304/"}, {"title": "Ganbara 2015-2016", "id": "/es/radio/radio-euskadi/ganbara/3446142/"}, {"title": "Ganbara 2014-2015", "id": "/es/radio/radio-euskadi/ganbara/2514892/"}, {"title": "Ganbara 2013-2014", "id": "/es/radio/radio-euskadi/ganbara/1411290/"}, {"title": "Ganbara 2012-2013", "id": "/es/radio/radio-euskadi/ganbara/945033/"}, {"title": "Ganbara 2011-2012", "id": "/es/radio/radio-euskadi/ganbara/912606/"}, {"title": "Ganbara de Cerca 2022-2023", "id": "/es/radio/radio-euskadi/ganbara-de-cerca/8909161/"}, {"title": "Ganbara de Cerca 2021-2022", "id": "/es/radio/radio-euskadi/ganbara-de-cerca/8446099/"}, {"title": "Gazteako podcastak 2020-2021", "id": "/eu/irratia/gaztea/gazteako-podcastak/7473390/"}, {"title": "Gazteako podcastak 2019-2020", "id": "/eu/irratia/gaztea/gazteako-podcastak/6564528/"}, {"title": "Generaci\u00f3n vinilo Generaci\u00f3n vinilo 2022- 2023", "id": "/es/radio/radio-vitoria/generacion-vinilo/8947610/"}, {"title": "Generaci\u00f3n vinilo Generaci\u00f3n vinilo 2021-2022", "id": "/es/radio/radio-vitoria/generacion-vinilo/8242645/"}, {"title": "Generaci\u00f3n vinilo 2020-2021", "id": "/es/radio/radio-vitoria/generacion-vinilo/7615292/"}, {"title": "Goiz kronika asteburua 2022-2023", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/8912859/"}, {"title": "Goiz kronika asteburua 2021-2022", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/8281586/"}, {"title": "Goiz kronika asteburua 2020-2021", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/7467765/"}, {"title": "Goiz kronika asteburua 2019-2020", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/6637251/"}, {"title": "Goiz kronika asteburua Goiz Kronika Asteburua 2018-2019", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/5826994/"}, {"title": "Goiz kronika asteburua 2017-2018", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/5050389/"}, {"title": "Goiz kronika asteburua 2016-2017", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/4343182/"}, {"title": "Goiz kronika asteburua 2015-2016", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/3609618/"}, {"title": "Goiz kronika asteburua 2014", "id": "/eu/irratia/euskadi-irratia/goiz-kronika-asteburua/2035486/"}, {"title": "Goizak gaur 2022-2023", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/8912864/"}, {"title": "Goizak gaur 2021-2022", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/8281588/"}, {"title": "Goizak gaur 2020-2021", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/7467771/"}, {"title": "Goizak gaur 2019-2020", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/6637253/"}, {"title": "Goizak gaur Goizak Gaur 2018-2019", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/5834130/"}, {"title": "Goizak gaur 2017-2018", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/5050382/"}, {"title": "Goizak gaur 2016-2017", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/4343188/"}, {"title": "Goizak gaur 2015-2016", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/3448248/"}, {"title": "Goizak gaur 2014-2015", "id": "/eu/irratia/euskadi-irratia/goizak-gaur/2794660/"}, {"title": "Goizak gaur asteburua 2022-2023", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/8912869/"}, {"title": "Goizak gaur asteburua 2021-2022", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/8281590/"}, {"title": "Goizak gaur asteburua 2020-2021", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/7467786/"}, {"title": "Goizak gaur asteburua 2019-2020", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/6637258/"}, {"title": "Goizak gaur asteburua 2018-2019", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/5827013/"}, {"title": "Goizak gaur asteburua 2017-2018", "id": "/eu/irratia/euskadi-irratia/goizak-gaur-asteburua/5093398/"}, {"title": "Gu GU 2022-2023", "id": "/eu/irratia/gaztea/gu/8912953/"}, {"title": "Gu 2021-2022", "id": "/eu/irratia/gaztea/gu/8281650/"}, {"title": "Gu 2020-2021", "id": "/eu/irratia/gaztea/gu/7473283/"}, {"title": "Gu 2019-2020", "id": "/eu/irratia/gaztea/gu/6643017/"}, {"title": "Gu onenak 2022-2023", "id": "/eu/irratia/gaztea/gu-onenak/8912955/"}, {"title": "Gu onenak 2021-2022", "id": "/eu/irratia/gaztea/gu-onenak/8281652/"}, {"title": "Gu onenak 2020-2021", "id": "/eu/irratia/gaztea/gu-onenak/7473368/"}, {"title": "Gu onenak 2019-2020", "id": "/eu/irratia/gaztea/gu-onenak/6709363/"}, {"title": "Gure musika 2018-2019", "id": "/es/radio/radio-vitoria/gure-musika/5837075/"}, {"title": "Gure musika 2017-2018", "id": "/es/radio/radio-vitoria/gure-musika/5044343/"}, {"title": "Gure musika 2016-2017", "id": "/es/radio/radio-vitoria/gure-musika/4320914/"}, {"title": "Gure musika 2015-2016", "id": "/es/radio/radio-vitoria/gure-musika/3434430/"}, {"title": "Gure musika 2014-2015", "id": "/es/radio/radio-vitoria/gure-musika/2493924/"}, {"title": "Gure musika 2013-2014", "id": "/es/radio/radio-vitoria/gure-musika/1481106/"}, {"title": "H\u00e1gase la luz 2022-2023", "id": "/es/radio/radio-euskadi/hagase-la-luz/8909164/"}, {"title": "H\u00e1gase la luz H\u00e1gase la luz 2021-2022", "id": "/es/radio/radio-euskadi/hagase-la-luz/8218153/"}, {"title": "H\u00e1gase la luz H\u00e1gase la luz 2020-2021", "id": "/es/radio/radio-euskadi/hagase-la-luz/7465942/"}, {"title": "H\u00e1gase la luz 2019-2020", "id": "/es/radio/radio-euskadi/hagase-la-luz/6629478/"}, {"title": "H\u00e1gase la luz 2018-2019", "id": "/es/radio/radio-euskadi/hagase-la-luz/5769425/"}, {"title": "H\u00e1gase la luz 2017-2018", "id": "/es/radio/radio-euskadi/hagase-la-luz/5049696/"}, {"title": "H\u00e1gase la luz 2016-2017", "id": "/es/radio/radio-euskadi/hagase-la-luz/4344202/"}, {"title": "H\u00e1gase la luz 2015-2016", "id": "/es/radio/radio-euskadi/hagase-la-luz/3467466/"}, {"title": "H\u00e1gase la luz 2014-2015", "id": "/es/radio/radio-euskadi/hagase-la-luz/2514868/"}, {"title": "H\u00e1gase la luz 2013-2014", "id": "/es/radio/radio-euskadi/hagase-la-luz/1411296/"}, {"title": "H\u00e1gase la luz 2012-2013", "id": "/es/radio/radio-euskadi/hagase-la-luz/945014/"}, {"title": "H\u00e1gase la luz 2011-2012", "id": "/es/radio/radio-euskadi/hagase-la-luz/912637/"}, {"title": "Herrian 2022-2023", "id": "/es/radio/radio-vitoria/herrian/8968239/"}, {"title": "Herrian Herrian 2021-2022", "id": "/es/radio/radio-vitoria/herrian/8242661/"}, {"title": "Herrian 2020-2021", "id": "/es/radio/radio-vitoria/herrian/7414914/"}, {"title": "Herrian 2019-2020", "id": "/es/radio/radio-vitoria/herrian/6647802/"}, {"title": "Herrian 2018-2019", "id": "/es/radio/radio-vitoria/herrian/5837085/"}, {"title": "Herrian 2017-2018", "id": "/es/radio/radio-vitoria/herrian/5044174/"}, {"title": "Herrian 2016-2017", "id": "/es/radio/radio-vitoria/herrian/4365602/"}, {"title": "Herritmia HERRITMIA 2020-2021", "id": "/eu/irratia/euskadi-irratia/herritmia/8157167/"}, {"title": "Herritmia 2019-2020", "id": "/eu/irratia/euskadi-irratia/herritmia/7294266/"}, {"title": "Herritmia 2021-2022", "id": "/eu/irratia/euskadi-irratia/herritmia/8281592/"}, {"title": "Hiru Erregeen Mahaia 2022-2023", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/8912871/"}, {"title": "Hiru Erregeen Mahaia 2021-2022", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/8281594/"}, {"title": "Hiru Erregeen Mahaia 2020-2021", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/7467818/"}, {"title": "Hiru Erregeen Mahaia 2019-2020", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/6637262/"}, {"title": "Hiru Erregeen Mahaia 2018-2019", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/5827044/"}, {"title": "Hiru Erregeen Mahaia 2017-2018", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/5052266/"}, {"title": "Hiru Erregeen Mahaia 2016-2017", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/4343274/"}, {"title": "Hiru Erregeen Mahaia 2015-2016", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/3445650/"}, {"title": "Hiru Erregeen Mahaia 2013-2014", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/1426172/"}, {"title": "Hiru Erregeen Mahaia 2014-2015", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/2515402/"}, {"title": "Hiru Erregeen Mahaia 2012-2013", "id": "/eu/irratia/euskadi-irratia/hiru-erregeen-mahaia/911186/"}, {"title": "Historias de cerca 2021-2022", "id": "/es/radio/radio-euskadi/historias-de-cerca/8824883/"}, {"title": "Historias de cerca Historias de cerca en \u00c1lava", "id": "/es/radio/radio-vitoria/historias-de-cerca/8240939/"}, {"title": "Historias de cerca 2020-2021", "id": "/es/radio/radio-euskadi/historias-de-cerca/7698082/"}, {"title": "Historias de cerca 2019-2020", "id": "/es/radio/radio-euskadi/historias-de-cerca/6629491/"}, {"title": "Historias de cerca historias de cerca 2018-2019", "id": "/es/radio/radio-euskadi/historias-de-cerca/6385467/"}, {"title": "Hitza Jolas 2022-2023", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/8913732/"}, {"title": "Hitza Jolas 2021-2022", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/8281596/"}, {"title": "Hitza Jolas 2020-2021", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/7467843/"}, {"title": "Hitza Jolas 2019-2020", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/6637264/"}, {"title": "Hitza Jolas 2018-2019", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/5834273/"}, {"title": "Hitza Jolas 2017-2018", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/5049915/"}, {"title": "Hitza Jolas 2016-2017", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/4343340/"}, {"title": "Hitza Jolas 2015-2016", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/3445558/"}, {"title": "Hitza Jolas 2014-2015", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/2515294/"}, {"title": "Hitza Jolas 2013-2014", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/1382702/"}, {"title": "Hitza Jolas 2012-2013", "id": "/eu/irratia/euskadi-irratia/hitza-jolas/911198/"}, {"title": "Iflandia Iflandia 2020-2021", "id": "/es/radio/radio-euskadi/iflandia/7465956/"}, {"title": "Iflandia 2019-2020", "id": "/es/radio/radio-euskadi/iflandia/6630964/"}, {"title": "Iflandia 2018-2019", "id": "/es/radio/radio-euskadi/iflandia/5769427/"}, {"title": "Iflandia 2017-2018", "id": "/es/radio/radio-euskadi/iflandia/5049675/"}, {"title": "Iflandia 2016-2017", "id": "/es/radio/radio-euskadi/iflandia/4344286/"}, {"title": "Iflandia 2015-2016", "id": "/es/radio/radio-euskadi/iflandia/3446192/"}, {"title": "Iflandia 2014-2015", "id": "/es/radio/radio-euskadi/iflandia/2514940/"}, {"title": "Iflandia 2013-2014", "id": "/es/radio/radio-euskadi/iflandia/1414310/"}, {"title": "Kantu Kontari 2022-2023", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/8912873/"}, {"title": "Kantu Kontari 2021-2022", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/8281598/"}, {"title": "Kantu Kontari 2020-21", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/7467845/"}, {"title": "Kantu Kontari 2019-2020", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/6637274/"}, {"title": "Kantu Kontari 2018-2019", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/5834367/"}, {"title": "Kantu Kontari 2017-2018", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/5052287/"}, {"title": "Kantu Kontari 2016-2017", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/4343328/"}, {"title": "Kantu Kontari 2015-2016", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/3445582/"}, {"title": "Kantu Kontari 2014-2015", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/2515330/"}, {"title": "Kantu Kontari 2013-2014", "id": "/eu/irratia/euskadi-irratia/kantu-kontari/1392832/"}, {"title": "Kantxa 2020-2021", "id": "/es/radio/radio-vitoria/kantxa/7415040/"}, {"title": "Kantxa 2019-2020", "id": "/es/radio/radio-vitoria/kantxa/6647804/"}, {"title": "Kantxa 2018-2019", "id": "/es/radio/radio-vitoria/kantxa/5837087/"}, {"title": "Kantxa 2017-2018", "id": "/es/radio/radio-vitoria/kantxa/5061344/"}, {"title": "Katedra 2022-2023", "id": "/eu/irratia/euskadi-irratia/katedra/8912877/"}, {"title": "Katedra 2021-2022", "id": "/eu/irratia/euskadi-irratia/katedra/8281600/"}, {"title": "Katedra 2020-21", "id": "/eu/irratia/euskadi-irratia/katedra/7467854/"}, {"title": "Katedra 2019-2020", "id": "/eu/irratia/euskadi-irratia/katedra/6637276/"}, {"title": "Katedra 2018-2019", "id": "/eu/irratia/euskadi-irratia/katedra/5834370/"}, {"title": "Katedra 2017-2018", "id": "/eu/irratia/euskadi-irratia/katedra/5050342/"}, {"title": "Katedra 2016-2017", "id": "/eu/irratia/euskadi-irratia/katedra/4343306/"}, {"title": "Katedra 2015-2016", "id": "/eu/irratia/euskadi-irratia/katedra/3445618/"}, {"title": "Katedra 2014-2015", "id": "/eu/irratia/euskadi-irratia/katedra/2515372/"}, {"title": "Katedra 2012-2013", "id": "/eu/irratia/euskadi-irratia/katedra/945136/"}, {"title": "Kirol Gaua", "id": "/es/radio/radio-euskadi/kirol-gaua/8908922/"}, {"title": "Kirol legez 2022-2023", "id": "/eu/irratia/euskadi-irratia/kirol-legez/8912879/"}, {"title": "Kirol legez 2021-2022", "id": "/eu/irratia/euskadi-irratia/kirol-legez/8281602/"}, {"title": "Kirol legez 2020-2021", "id": "/eu/irratia/euskadi-irratia/kirol-legez/7467901/"}, {"title": "Kirol legez 2019-2020", "id": "/eu/irratia/euskadi-irratia/kirol-legez/6637278/"}, {"title": "Kirol legez 2018-2019", "id": "/eu/irratia/euskadi-irratia/kirol-legez/5834375/"}, {"title": "Kirol legez 2017-2018", "id": "/eu/irratia/euskadi-irratia/kirol-legez/5050368/"}, {"title": "Kirol legez 2016-2017", "id": "/eu/irratia/euskadi-irratia/kirol-legez/4343256/"}, {"title": "Kirol legez 2015-2016", "id": "/eu/irratia/euskadi-irratia/kirol-legez/3445662/"}, {"title": "Kirol legez 2013-2014", "id": "/eu/irratia/euskadi-irratia/kirol-legez/2169900/"}, {"title": "Kirol legez 2014-2015", "id": "/eu/irratia/euskadi-irratia/kirol-legez/2515432/"}, {"title": "Kirol legez 2012-2013", "id": "/eu/irratia/euskadi-irratia/kirol-legez/958535/"}, {"title": "Kirolaldia 2022-2023", "id": "/es/radio/radio-euskadi/kirolaldia/8909182/"}, {"title": "Kirolaldia 2021-2022", "id": "/es/radio/radio-euskadi/kirolaldia/8218166/"}, {"title": "Kirolaldia 2020-2021", "id": "/es/radio/radio-euskadi/kirolaldia/7465966/"}, {"title": "Kirolaldia 2019-2020", "id": "/es/radio/radio-euskadi/kirolaldia/6630966/"}, {"title": "Kirolaldia 2018-2019", "id": "/es/radio/radio-euskadi/kirolaldia/5769442/"}, {"title": "Kirolaldia 2017-2018", "id": "/es/radio/radio-euskadi/kirolaldia/5051836/"}, {"title": "Kirolaldia 2 2022-2023", "id": "/es/radio/radio-euskadi/kirolaldia-2/8909170/"}, {"title": "Kirolaldia 2 2021-2022", "id": "/es/radio/radio-euskadi/kirolaldia-2/8409749/"}, {"title": "Klasikoak EITBn 2014", "id": "/eu/irratia/eitb-musika/klasikoak-eitbn/1888844/"}, {"title": "Klasikoak EITBn 2013", "id": "/eu/irratia/eitb-musika/klasikoak-eitbn/1233440/"}, {"title": "Kultur leihoa 2022-2023", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/8912881/"}, {"title": "Kultur leihoa 2021-2022", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/8281604/"}, {"title": "Kultur leihoa 2020-2021", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/7467867/"}, {"title": "Kultur leihoa 2019-2020", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/6637283/"}, {"title": "Kultur leihoa 2018-2019", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/5834378/"}, {"title": "Kultur leihoa 2017-2018", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/5050371/"}, {"title": "Kultur leihoa 2016-2017", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/4343236/"}, {"title": "Kultur leihoa 2015-2016", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/3445692/"}, {"title": "Kultur leihoa 2014-2015", "id": "/eu/irratia/euskadi-irratia/kultur-leihoa/3257958/"}, {"title": "Kultura.eus 2022-2023", "id": "/es/radio/radio-euskadi/kulturaeus/8913853/"}, {"title": "Kultura.eus 2021-2022", "id": "/es/radio/radio-euskadi/kulturaeus/8218179/"}, {"title": "Kultura.eus 2020-2021", "id": "/es/radio/radio-euskadi/kulturaeus/7466465/"}, {"title": "Kultura.eus 2019-2020", "id": "/es/radio/radio-euskadi/kulturaeus/6630976/"}, {"title": "Kultura.eus 2018-2019", "id": "/es/radio/radio-euskadi/kulturaeus/5769455/"}, {"title": "Kultura.eus 2017-2018", "id": "/es/radio/radio-euskadi/kulturaeus/5049693/"}, {"title": "Kultura.eus 2016-2017", "id": "/es/radio/radio-euskadi/kulturaeus/4344222/"}, {"title": "Kultura.eus 2015-2016", "id": "/es/radio/radio-euskadi/kulturaeus/3446246/"}, {"title": "Kultura.eus 2014-2015", "id": "/es/radio/radio-euskadi/kulturaeus/3260460/"}, {"title": "La Casa de la Palabra La casa de la palabra 2020 - 2021", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/7466421/"}, {"title": "La Casa de la Palabra 2019-2020", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/6630981/"}, {"title": "La Casa de la Palabra 2018-2019", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/5769457/"}, {"title": "La Casa de la Palabra 2017-2018", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/5049644/"}, {"title": "La Casa de la Palabra 2016-2017", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/4345986/"}, {"title": "La Casa de la Palabra 2015-2016", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/3446030/"}, {"title": "La Casa de la Palabra 2014-2015", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/2514820/"}, {"title": "La Casa de la Palabra 2013-2014", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/1411308/"}, {"title": "La Casa de la Palabra 2012-2013", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/945034/"}, {"title": "La Casa de la Palabra 2011-2012", "id": "/es/radio/radio-euskadi/la-casa-de-la-palabra/912604/"}, {"title": "La fiaca 2022-2023", "id": "/es/radio/radio-vitoria/la-fiaca/8949985/"}, {"title": "La fiaca 2021-2022", "id": "/es/radio/radio-vitoria/la-fiaca/8296709/"}, {"title": "La fiaca 2020-2021", "id": "/es/radio/radio-vitoria/la-fiaca/7415051/"}, {"title": "La fiaca 2019-2020", "id": "/es/radio/radio-vitoria/la-fiaca/6647814/"}, {"title": "La fiaca 2018-2019", "id": "/es/radio/radio-vitoria/la-fiaca/5837090/"}, {"title": "La fiaca 2017-2018", "id": "/es/radio/radio-vitoria/la-fiaca/5044202/"}, {"title": "La fiaca 2016-2017", "id": "/es/radio/radio-vitoria/la-fiaca/4331404/"}, {"title": "La fiaca 2015-2016", "id": "/es/radio/radio-vitoria/la-fiaca/3434436/"}, {"title": "La fiaca 2014-2015", "id": "/es/radio/radio-vitoria/la-fiaca/2493930/"}, {"title": "La fiaca 2013-2014", "id": "/es/radio/radio-vitoria/la-fiaca/1481312/"}, {"title": "La fiaca 2012-2013", "id": "/es/radio/radio-vitoria/la-fiaca/911016/"}, {"title": "La Galer\u00eda 2022-2023", "id": "/es/radio/radio-euskadi/la-galeria/8913856/"}, {"title": "La Galer\u00eda La galer\u00eda 2021-2022", "id": "/es/radio/radio-euskadi/la-galeria/8218182/"}, {"title": "La Galer\u00eda La galer\u00eda 2020-2021", "id": "/es/radio/radio-euskadi/la-galeria/7466423/"}, {"title": "La Galer\u00eda 2019-2020", "id": "/es/radio/radio-euskadi/la-galeria/6629341/"}, {"title": "La Galer\u00eda 2018-2019", "id": "/es/radio/radio-euskadi/la-galeria/5769468/"}, {"title": "La Galer\u00eda 2017-2018", "id": "/es/radio/radio-euskadi/la-galeria/5049653/"}, {"title": "La Galer\u00eda 2016-2017", "id": "/es/radio/radio-euskadi/la-galeria/4345800/"}, {"title": "La Galer\u00eda 2015-2016", "id": "/es/radio/radio-euskadi/la-galeria/3446036/"}, {"title": "La Galer\u00eda 2014-2015", "id": "/es/radio/radio-euskadi/la-galeria/2514826/"}, {"title": "La Galer\u00eda 2013-2014", "id": "/es/radio/radio-euskadi/la-galeria/1411312/"}, {"title": "La Galer\u00eda 2012-2013", "id": "/es/radio/radio-euskadi/la-galeria/945016/"}, {"title": "La Galer\u00eda 2011-2012", "id": "/es/radio/radio-euskadi/la-galeria/912634/"}, {"title": "LA GRADA", "id": "/es/radio/radio-euskadi/la-grada/8942676/"}, {"title": "La Jungla Sonora 2022-2023", "id": "/es/radio/radio-euskadi/la-jungla-sonora/8913863/"}, {"title": "La Jungla Sonora 2021-2022", "id": "/es/radio/radio-euskadi/la-jungla-sonora/8218187/"}, {"title": "La Jungla Sonora LA jungla sonora 2020-2021", "id": "/es/radio/radio-euskadi/la-jungla-sonora/7466425/"}, {"title": "La Jungla Sonora 2019-2020", "id": "/es/radio/radio-euskadi/la-jungla-sonora/6630983/"}, {"title": "La Jungla Sonora 2018-2019", "id": "/es/radio/radio-euskadi/la-jungla-sonora/5769474/"}, {"title": "La Jungla Sonora 2017-2018", "id": "/es/radio/radio-euskadi/la-jungla-sonora/5049669/"}, {"title": "La Jungla Sonora 2016-2017", "id": "/es/radio/radio-euskadi/la-jungla-sonora/4344298/"}, {"title": "La Jungla Sonora 2015-2016", "id": "/es/radio/radio-euskadi/la-jungla-sonora/3446154/"}, {"title": "La Jungla Sonora 2014-2015", "id": "/es/radio/radio-euskadi/la-jungla-sonora/2514910/"}, {"title": "La Jungla Sonora 2013-2014", "id": "/es/radio/radio-euskadi/la-jungla-sonora/1411314/"}, {"title": "La Jungla Sonora 2012-2013", "id": "/es/radio/radio-euskadi/la-jungla-sonora/945038/"}, {"title": "La Jungla Sonora 2011-2012", "id": "/es/radio/radio-euskadi/la-jungla-sonora/911178/"}, {"title": "La Mec\u00e1nica del caracol 2022-2023", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/8913871/"}, {"title": "La Mec\u00e1nica del caracol La mec\u00e1nica del caracol 2021-2022", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/8175376/"}, {"title": "La Mec\u00e1nica del caracol La mec\u00e1nica del caracol 2020-2021", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/7466427/"}, {"title": "La Mec\u00e1nica del caracol 2019-2020", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/6630989/"}, {"title": "La Mec\u00e1nica del caracol 2018-2019", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/5769476/"}, {"title": "La Mec\u00e1nica del caracol 2017-2018", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/5050047/"}, {"title": "La Mec\u00e1nica del caracol 2015-2016", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/3446060/"}, {"title": "La Mec\u00e1nica del caracol 2014-2015", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/2514856/"}, {"title": "La Mec\u00e1nica del caracol 2013-2014", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/1411318/"}, {"title": "La Mec\u00e1nica del caracol 2012-2013", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/945042/"}, {"title": "La Mec\u00e1nica del caracol 2011-2012", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/911170/"}, {"title": "La Mec\u00e1nica del caracol 2016-2017", "id": "/es/radio/radio-euskadi/la-mecanica-del-caracol/4345846/"}, {"title": "La Ruta Slow 2022-2023", "id": "/es/radio/radio-vitoria/la-ruta-slow/8949977/"}, {"title": "La Ruta Slow 2021-2022", "id": "/es/radio/radio-vitoria/la-ruta-slow/8296711/"}, {"title": "La Ruta Slow 2020-2021", "id": "/es/radio/radio-vitoria/la-ruta-slow/7415053/"}, {"title": "La Ruta Slow 2019-2020", "id": "/es/radio/radio-vitoria/la-ruta-slow/6649511/"}, {"title": "La Ruta Slow 2018-2019", "id": "/es/radio/radio-vitoria/la-ruta-slow/5834761/"}, {"title": "La Ruta Slow 2017-2018", "id": "/es/radio/radio-vitoria/la-ruta-slow/5044177/"}, {"title": "La Ruta Slow 2016-2017", "id": "/es/radio/radio-vitoria/la-ruta-slow/4331468/"}, {"title": "La Ruta Slow 2015-2016) R", "id": "/es/radio/radio-vitoria/la-ruta-slow/3434448/"}, {"title": "La Ruta Slow 2014-2015", "id": "/es/radio/radio-vitoria/la-ruta-slow/2493942/"}, {"title": "La Ruta Slow 2013-2014", "id": "/es/radio/radio-vitoria/la-ruta-slow/1481366/"}, {"title": "La Ruta Slow 2012-2013", "id": "/es/radio/radio-vitoria/la-ruta-slow/1799742/"}, {"title": "La Terraza 2022-2023", "id": "/es/radio/radio-euskadi/la-terraza/8915101/"}, {"title": "La Terraza 2021-2022", "id": "/es/radio/radio-euskadi/la-terraza/8218879/"}, {"title": "Landaberri 2022-2023", "id": "/eu/irratia/euskadi-irratia/landaberri/8912896/"}, {"title": "Landaberri 2021-2022", "id": "/eu/irratia/euskadi-irratia/landaberri/8281606/"}, {"title": "Landaberri 2020-2021", "id": "/eu/irratia/euskadi-irratia/landaberri/7467869/"}, {"title": "Landaberri 2019-2020", "id": "/eu/irratia/euskadi-irratia/landaberri/6637285/"}, {"title": "Landaberri 2018-2019", "id": "/eu/irratia/euskadi-irratia/landaberri/5834380/"}, {"title": "Landaberri 2017-2018", "id": "/eu/irratia/euskadi-irratia/landaberri/5050345/"}, {"title": "Landaberri 2016-2017", "id": "/eu/irratia/euskadi-irratia/landaberri/4343300/"}, {"title": "Landaberri 2015-2016", "id": "/eu/irratia/euskadi-irratia/landaberri/3445624/"}, {"title": "Landaberri 2014-2015", "id": "/eu/irratia/euskadi-irratia/landaberri/2515378/"}, {"title": "Landaberri 2013-2014", "id": "/eu/irratia/euskadi-irratia/landaberri/1407850/"}, {"title": "Landaberri 2012-2013", "id": "/eu/irratia/euskadi-irratia/landaberri/911214/"}, {"title": "Levando Anclas 2022-2023", "id": "/es/radio/radio-euskadi/levando-anclas/8915103/"}, {"title": "Levando Anclas 2021-2022", "id": "/es/radio/radio-euskadi/levando-anclas/8218189/"}, {"title": "Levando Anclas Levando anclas 2020-2021", "id": "/es/radio/radio-euskadi/levando-anclas/7466430/"}, {"title": "Levando Anclas 2019-2020", "id": "/es/radio/radio-euskadi/levando-anclas/6630991/"}, {"title": "Levando Anclas 2018-2019", "id": "/es/radio/radio-euskadi/levando-anclas/5769484/"}, {"title": "Levando Anclas 2017-2018", "id": "/es/radio/radio-euskadi/levando-anclas/5049672/"}, {"title": "Levando Anclas 2016-2017", "id": "/es/radio/radio-euskadi/levando-anclas/4344292/"}, {"title": "Levando Anclas 2015-2016", "id": "/es/radio/radio-euskadi/levando-anclas/3446172/"}, {"title": "Levando Anclas 2014-2015", "id": "/es/radio/radio-euskadi/levando-anclas/2514928/"}, {"title": "Levando Anclas 2013-2014", "id": "/es/radio/radio-euskadi/levando-anclas/1411324/"}, {"title": "Levando Anclas 2012-2013", "id": "/es/radio/radio-euskadi/levando-anclas/945004/"}, {"title": "Levando Anclas 2011-2012", "id": "/es/radio/radio-euskadi/levando-anclas/912775/"}, {"title": "Lizardiren baratza 2022-2023", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/8912898/"}, {"title": "Lizardiren baratza 2021-2022", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/8281608/"}, {"title": "Lizardiren baratza 2020-2021", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/7467874/"}, {"title": "Lizardiren baratza 2019-2020", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/6637287/"}, {"title": "Lizardiren baratza Lizardiren Baratza 2018-2019", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/5834390/"}, {"title": "Lizardiren baratza 2017-2018", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/5049968/"}, {"title": "Lizardiren baratza 2016-2017", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/4343334/"}, {"title": "Lizardiren baratza 2015-2016", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/3445564/"}, {"title": "Lizardiren baratza 2014-2015", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/2515306/"}, {"title": "Lizardiren baratza 2013-2014", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/1382704/"}, {"title": "Lizardiren baratza 2012-2013", "id": "/eu/irratia/euskadi-irratia/lizardiren-baratza/911218/"}, {"title": "Lur Bizia 2022-2023", "id": "/es/radio/radio-vitoria/lur-bizia/8949972/"}, {"title": "Lur Bizia 2021-2022", "id": "/es/radio/radio-vitoria/lur-bizia/8242670/"}, {"title": "Lur Bizia Lur Bizia /2020-2021)", "id": "/es/radio/radio-vitoria/lur-bizia/7414904/"}, {"title": "Lur Bizia 2019-2020", "id": "/es/radio/radio-vitoria/lur-bizia/6649553/"}, {"title": "Lur Bizia 2018-2019", "id": "/es/radio/radio-vitoria/lur-bizia/5834749/"}, {"title": "Lur Bizia 2017-2018", "id": "/es/radio/radio-vitoria/lur-bizia/5044450/"}, {"title": "Lur Bizia 2016-2017", "id": "/es/radio/radio-vitoria/lur-bizia/4320802/"}, {"title": "Lur Bizia 2015-2016", "id": "/es/radio/radio-vitoria/lur-bizia/3434560/"}, {"title": "Lur Bizia 2014-2015", "id": "/es/radio/radio-vitoria/lur-bizia/2558344/"}, {"title": "M\u00e1s que palabras 2022-2023", "id": "/es/radio/radio-euskadi/mas-que-palabras/8915106/"}, {"title": "M\u00e1s que palabras M\u00e1s que palabras 2021-2022", "id": "/es/radio/radio-euskadi/mas-que-palabras/8220046/"}, {"title": "M\u00e1s que palabras M\u00e1s que palabras 2020-2021", "id": "/es/radio/radio-euskadi/mas-que-palabras/7466446/"}, {"title": "M\u00e1s que palabras 2019-2020", "id": "/es/radio/radio-euskadi/mas-que-palabras/6630998/"}, {"title": "M\u00e1s que palabras 2018-2019", "id": "/es/radio/radio-euskadi/mas-que-palabras/5769500/"}, {"title": "M\u00e1s que palabras 2017-2018", "id": "/es/radio/radio-euskadi/mas-que-palabras/5049647/"}, {"title": "M\u00e1s que palabras 2016-2017", "id": "/es/radio/radio-euskadi/mas-que-palabras/4345974/"}, {"title": "M\u00e1s que palabras 2015-2016", "id": "/es/radio/radio-euskadi/mas-que-palabras/3446048/"}, {"title": "M\u00e1s que palabras 2014-2015", "id": "/es/radio/radio-euskadi/mas-que-palabras/2514832/"}, {"title": "M\u00e1s que palabras 2013-2014", "id": "/es/radio/radio-euskadi/mas-que-palabras/1411328/"}, {"title": "M\u00e1s que palabras 2012-2013", "id": "/es/radio/radio-euskadi/mas-que-palabras/945019/"}, {"title": "M\u00e1s que palabras 2011-2012", "id": "/es/radio/radio-euskadi/mas-que-palabras/912614/"}, {"title": "Mezularia 2022-2023", "id": "/eu/irratia/euskadi-irratia/mezularia/8912902/"}, {"title": "Mezularia 2021-2022", "id": "/eu/irratia/euskadi-irratia/mezularia/8281612/"}, {"title": "Mezularia 2020-2021", "id": "/eu/irratia/euskadi-irratia/mezularia/7467876/"}, {"title": "Mezularia 2019-2020", "id": "/eu/irratia/euskadi-irratia/mezularia/6637289/"}, {"title": "Mezularia 2018-2019", "id": "/eu/irratia/euskadi-irratia/mezularia/5834392/"}, {"title": "Mezularia 2017-2018", "id": "/eu/irratia/euskadi-irratia/mezularia/5052252/"}, {"title": "Mezularia 2016-2017", "id": "/eu/irratia/euskadi-irratia/mezularia/4343268/"}, {"title": "Mezularia 2015-2016", "id": "/eu/irratia/euskadi-irratia/mezularia/3445656/"}, {"title": "Mezularia 2014-2015", "id": "/eu/irratia/euskadi-irratia/mezularia/2515408/"}, {"title": "Mezularia 2013-2014", "id": "/eu/irratia/euskadi-irratia/mezularia/1467594/"}, {"title": "Mezularia 2012-2013", "id": "/eu/irratia/euskadi-irratia/mezularia/911150/"}, {"title": "Norteko Ferrokarrilla 2022-2023", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/8912904/"}, {"title": "Norteko Ferrokarrilla 2021-2022", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/8281622/"}, {"title": "Norteko Ferrokarrilla 2020-2021", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/7467881/"}, {"title": "Norteko Ferrokarrilla 2019-2020", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/6637292/"}, {"title": "Norteko Ferrokarrilla 2018-2019", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/5834402/"}, {"title": "Norteko Ferrokarrilla 2017-2018", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/5049965/"}, {"title": "Norteko Ferrokarrilla 2016-2017", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/4343346/"}, {"title": "Norteko Ferrokarrilla 2015-2016", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/3445552/"}, {"title": "Norteko Ferrokarrilla 2014-2015", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/2515288/"}, {"title": "Norteko Ferrokarrilla 2013-2014", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/1382698/"}, {"title": "Norteko Ferrokarrilla 2012-2013", "id": "/eu/irratia/euskadi-irratia/norteko-ferrokarrilla/911182/"}, {"title": "Opera on 2022 -2023", "id": "/es/radio/radio-vitoria/opera-on/8870679/"}, {"title": "Opera on 2021-2022", "id": "/es/radio/radio-vitoria/opera-on/8242686/"}, {"title": "Opera on 2020-2021", "id": "/es/radio/radio-vitoria/opera-on/7326863/"}, {"title": "Opera on 2019-2020", "id": "/es/radio/radio-vitoria/opera-on/6649568/"}, {"title": "Opera on 2018-2019", "id": "/es/radio/radio-vitoria/opera-on/5837098/"}, {"title": "Opera on 2017-2018", "id": "/es/radio/radio-vitoria/opera-on/5044558/"}, {"title": "Opera on 2016-2017", "id": "/es/radio/radio-vitoria/opera-on/4320628/"}, {"title": "Opera on 2015-2016", "id": "/es/radio/radio-vitoria/opera-on/3815324/"}, {"title": "Osasun Etxea 2022-2023", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/8912911/"}, {"title": "Osasun Etxea 2021-2022", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/8281624/"}, {"title": "Osasun Etxea 2020-2021", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/7467883/"}, {"title": "Osasun Etxea 2019-2020", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/6637316/"}, {"title": "Osasun Etxea 2018-2019", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/5834404/"}, {"title": "Osasun Etxea 2017-2018", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/5050337/"}, {"title": "Osasun Etxea 2016-2017", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/4343312/"}, {"title": "Osasun Etxea 2015-2016", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/3445594/"}, {"title": "Osasun Etxea 2014-2015", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/2515360/"}, {"title": "Osasun Etxea 2013-2014", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/1399304/"}, {"title": "Osasun Etxea 2012-2013", "id": "/eu/irratia/euskadi-irratia/osasun-etxea/979204/"}, {"title": "Parlamento en las Ondas 2022-2023", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas/8915111/"}, {"title": "Parlamento en las Ondas Parlamento en las ondas 2021-2022", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas/8220054/"}, {"title": "Parlamento en las Ondas Parlamento en las ondas Euskadi 2020-2021", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas/7466449/"}, {"title": "Parlamento en las Ondas 2019-2020", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas/6631012/"}, {"title": "Parlamento en las Ondas 2018-2019", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas/5912752/"}, {"title": "Parlamento en las Ondas Navarra 2022-2023", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas-navarra/8915116/"}, {"title": "Parlamento en las Ondas Navarra Parlamento en las ondas Navarra 2021-2022", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas-navarra/8220064/"}, {"title": "Parlamento en las Ondas Navarra Parlamento en las ondas Navarra 2020-2021", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas-navarra/7466454/"}, {"title": "Parlamento en las Ondas Navarra 2019-2020", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas-navarra/6631014/"}, {"title": "Parlamento en las Ondas Navarra 2018-2019", "id": "/es/radio/radio-euskadi/parlamento-en-las-ondas-navarra/5928471/"}, {"title": "Pompas de papel 2022-2023", "id": "/es/radio/radio-euskadi/pompas-de-papel/8915120/"}, {"title": "Pompas de papel Pompas de papel 2021-2022", "id": "/es/radio/radio-euskadi/pompas-de-papel/8220069/"}, {"title": "Pompas de papel Pompas de papel 2020-2021", "id": "/es/radio/radio-euskadi/pompas-de-papel/7466456/"}, {"title": "Pompas de papel 2019-2020", "id": "/es/radio/radio-euskadi/pompas-de-papel/6631068/"}, {"title": "Pompas de papel 2018-2019", "id": "/es/radio/radio-euskadi/pompas-de-papel/5769507/"}, {"title": "Pompas de papel 2017-2018", "id": "/es/radio/radio-euskadi/pompas-de-papel/5049650/"}, {"title": "Pompas de papel 2016-2017", "id": "/es/radio/radio-euskadi/pompas-de-papel/4345894/"}, {"title": "Pompas de papel 2015-2016", "id": "/es/radio/radio-euskadi/pompas-de-papel/3446054/"}, {"title": "Pompas de papel 2014-2015", "id": "/es/radio/radio-euskadi/pompas-de-papel/2514844/"}, {"title": "Pompas de papel 2013-2014", "id": "/es/radio/radio-euskadi/pompas-de-papel/1411332/"}, {"title": "Pompas de papel 2012-2013", "id": "/es/radio/radio-euskadi/pompas-de-papel/945007/"}, {"title": "Pompas de papel 2011-2012", "id": "/es/radio/radio-euskadi/pompas-de-papel/912770/"}, {"title": "Portobello 2022-2023", "id": "/eu/irratia/euskadi-irratia/portobello/8912916/"}, {"title": "Portobello 2021-2022", "id": "/eu/irratia/euskadi-irratia/portobello/8281626/"}, {"title": "Portobello 2020-2021", "id": "/eu/irratia/euskadi-irratia/portobello/7467885/"}, {"title": "Portobello 2019-2020", "id": "/eu/irratia/euskadi-irratia/portobello/6637318/"}, {"title": "Portobello Portobello 2018-2019", "id": "/eu/irratia/euskadi-irratia/portobello/5834417/"}, {"title": "Portobello 2017-2018", "id": "/eu/irratia/euskadi-irratia/portobello/5052566/"}, {"title": "Portobello 2016-2017", "id": "/eu/irratia/euskadi-irratia/portobello/4343200/"}, {"title": "Portobello 2015-2016", "id": "/eu/irratia/euskadi-irratia/portobello/3445846/"}, {"title": "Portobello 2014-2015", "id": "/eu/irratia/euskadi-irratia/portobello/2515276/"}, {"title": "Portobello 2013-2014", "id": "/eu/irratia/euskadi-irratia/portobello/1382684/"}, {"title": "Portobello 2012-2013", "id": "/eu/irratia/euskadi-irratia/portobello/911236/"}, {"title": "Prime Time 2022-2023", "id": "/es/radio/radio-vitoria/prime-time/8949969/"}, {"title": "Prime Time 2021-2022", "id": "/es/radio/radio-vitoria/prime-time/8296766/"}, {"title": "Prime Time 2020-2021", "id": "/es/radio/radio-vitoria/prime-time/7415058/"}, {"title": "Prime Time 2019-2020", "id": "/es/radio/radio-vitoria/prime-time/6649574/"}, {"title": "Prime Time 2018-2019", "id": "/es/radio/radio-vitoria/prime-time/5834745/"}, {"title": "Prime Time 2017-2018", "id": "/es/radio/radio-vitoria/prime-time/5104438/"}, {"title": "Radio Vitoria Deportes Actualidad 2022-2023", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/8949935/"}, {"title": "Radio Vitoria Deportes Actualidad RV Deportes Actualidad 2021-2022", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/8296723/"}, {"title": "Radio Vitoria Deportes Actualidad 2020-2021", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/7415062/"}, {"title": "Radio Vitoria Deportes Actualidad 2019 - 2020", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/6649578/"}, {"title": "Radio Vitoria Deportes Actualidad 2018 - 2019", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/5837036/"}, {"title": "Radio Vitoria Deportes Actualidad 2017 - 2018", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-actualidad/5091717/"}, {"title": "Radio Vitoria Deportes Magazine 2021-2022", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-magazine/8242763/"}, {"title": "Radio Vitoria Deportes Magazine 2020-2021", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-magazine/7415078/"}, {"title": "Radio Vitoria Deportes Magazine 2019-2020", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-magazine/6649585/"}, {"title": "Radio Vitoria Deportes Magazine 2018-2019", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-magazine/5837030/"}, {"title": "Radio Vitoria Deportes Magazine 2017-2018", "id": "/es/radio/radio-vitoria/radio-vitoria-deportes-magazine/5091933/"}, {"title": "Radio Vitoria Gaur", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur/8950952/"}, {"title": "Radio Vitoria Gaur actualidad RV Gaur Actualidad 2021-2022", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-actualidad/8296741/"}, {"title": "Radio Vitoria Gaur actualidad 2020-2021", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-actualidad/7415083/"}, {"title": "Radio Vitoria Gaur actualidad 2019-2020", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-actualidad/6649599/"}, {"title": "Radio Vitoria Gaur actualidad 2018-2019", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-actualidad/5837028/"}, {"title": "Radio Vitoria Gaur actualidad 2017-2018", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-actualidad/5061675/"}, {"title": "Radio Vitoria Gaur Magazine 2021-2022", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-magazine/8247207/"}, {"title": "Radio Vitoria Gaur Magazine 2020-2021", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-magazine/7415085/"}, {"title": "Radio Vitoria Gaur Magazine 2019-2020", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-magazine/6649604/"}, {"title": "Radio Vitoria Gaur Magazine 2018-2019", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-magazine/5842877/"}, {"title": "Radio Vitoria Gaur Magazine 2017-2018", "id": "/es/radio/radio-vitoria/radio-vitoria-gaur-magazine/5086650/"}, {"title": "RV JAZZ 2022-2023", "id": "/es/radio/radio-vitoria/rv-jazz/8949952/"}, {"title": "RV JAZZ 20021-2022", "id": "/es/radio/radio-vitoria/rv-jazz/8236229/"}, {"title": "RV JAZZ 2020-2021", "id": "/es/radio/radio-vitoria/rv-jazz/7529627/"}, {"title": "Sarean 2021-2022", "id": "/eu/irratia/euskadi-irratia/sarean/8281628/"}, {"title": "Sarean 2020-2021", "id": "/eu/irratia/euskadi-irratia/sarean/7467895/"}, {"title": "Sarean 2019-2020", "id": "/eu/irratia/euskadi-irratia/sarean/6637320/"}, {"title": "Sarean 2018-2019", "id": "/eu/irratia/euskadi-irratia/sarean/5836483/"}, {"title": "Sarean 2016-2017", "id": "/eu/irratia/euskadi-irratia/sarean/4929283/"}, {"title": "Si Nos Confiamos 2022-2023", "id": "/es/radio/radio-euskadi/si-nos-confiamos/8915129/"}, {"title": "Si Nos Confiamos Si nos confiamos 2021-2022", "id": "/es/radio/radio-euskadi/si-nos-confiamos/8220084/"}, {"title": "Si Nos Confiamos Si nos confiamos 2020-2021", "id": "/es/radio/radio-euskadi/si-nos-confiamos/7466458/"}, {"title": "Si Nos Confiamos 2019-2020", "id": "/es/radio/radio-euskadi/si-nos-confiamos/6631043/"}, {"title": "Si Nos Confiamos 2018-2019", "id": "/es/radio/radio-euskadi/si-nos-confiamos/5873612/"}, {"title": "Supercanasta Supercanasta 2022-2023", "id": "/es/radio/radio-vitoria/supercanasta/8949963/"}, {"title": "Supercanasta Supercanasta 2021-2022", "id": "/es/radio/radio-vitoria/supercanasta/8326972/"}, {"title": "Supercanasta 2020-2021", "id": "/es/radio/radio-vitoria/supercanasta/7415100/"}, {"title": "Supercanasta 2019-2020", "id": "/es/radio/radio-vitoria/supercanasta/6649606/"}, {"title": "Supercanasta 2018-2019", "id": "/es/radio/radio-vitoria/supercanasta/5833727/"}, {"title": "Supercanasta 2017-2018", "id": "/es/radio/radio-vitoria/supercanasta/5044189/"}, {"title": "Supercanasta 2016-2017", "id": "/es/radio/radio-vitoria/supercanasta/4331456/"}, {"title": "Supercanasta 2015-2016", "id": "/es/radio/radio-vitoria/supercanasta/3434466/"}, {"title": "Supercanasta 2014-2015", "id": "/es/radio/radio-vitoria/supercanasta/2493960/"}, {"title": "Supercanasta 2013-2014", "id": "/es/radio/radio-vitoria/supercanasta/1481500/"}, {"title": "Tenemos que hablar", "id": "/es/radio/radio-euskadi/tenemos-que-hablar/8295009/"}, {"title": "Top Dance 2018-2019", "id": "/eu/irratia/gaztea/top-dance/5847170/"}, {"title": "Top Dance 2017-2018", "id": "/eu/irratia/gaztea/top-dance/5036114/"}, {"title": "Top Dance 2016-2017", "id": "/eu/irratia/gaztea/top-dance/4418285/"}, {"title": "Top Dance 2015-2016", "id": "/eu/irratia/gaztea/top-dance/3847756/"}, {"title": "Top Gaztea Top Gaztea 2022-2023", "id": "/eu/irratia/gaztea/top-gaztea/8912959/"}, {"title": "Top Gaztea 2021-2022", "id": "/eu/irratia/gaztea/top-gaztea/8281656/"}, {"title": "Top Gaztea 2020-2021", "id": "/eu/irratia/gaztea/top-gaztea/7473387/"}, {"title": "Top Gaztea 2019-2020", "id": "/eu/irratia/gaztea/top-gaztea/6637374/"}, {"title": "Top Gaztea 2018-2019", "id": "/eu/irratia/gaztea/top-gaztea/5827066/"}, {"title": "Top Gaztea 2017-2018", "id": "/eu/irratia/gaztea/top-gaztea/5089111/"}, {"title": "Tostartean 2021-2022", "id": "/eu/irratia/euskadi-irratia/tostartean/8281630/"}, {"title": "Tostartean 2020-2021", "id": "/eu/irratia/euskadi-irratia/tostartean/7467918/"}, {"title": "Tostartean 2019-2020", "id": "/eu/irratia/euskadi-irratia/tostartean/6637203/"}, {"title": "Tostartean 2018-2019", "id": "/eu/irratia/euskadi-irratia/tostartean/5827080/"}, {"title": "Tostartean 2017-2018", "id": "/eu/irratia/euskadi-irratia/tostartean/5050379/"}, {"title": "Tostartean 2016-2017", "id": "/eu/irratia/euskadi-irratia/tostartean/4343212/"}, {"title": "Tostartean 2015-2016", "id": "/eu/irratia/euskadi-irratia/tostartean/3445704/"}, {"title": "Tostartean 2014-2015", "id": "/eu/irratia/euskadi-irratia/tostartean/3428230/"}, {"title": "Tostartean 2012-2013", "id": "/eu/irratia/euskadi-irratia/tostartean/911239/"}, {"title": "Tres puntos y a casa 2016-2017", "id": "/es/radio/radio-vitoria/tres-puntos-y-a-casa/4320902/"}, {"title": "Tres puntos y a casa 2015-2016", "id": "/es/radio/radio-vitoria/tres-puntos-y-a-casa/3434492/"}, {"title": "Tres puntos y a casa 2014-2015", "id": "/es/radio/radio-vitoria/tres-puntos-y-a-casa/2493984/"}, {"title": "Tres puntos y a casa 2013-2014", "id": "/es/radio/radio-vitoria/tres-puntos-y-a-casa/1521008/"}, {"title": "Vivir Para Ver 2022-2023", "id": "/es/radio/radio-euskadi/vivir-para-ver/8915134/"}, {"title": "Vivir Para Ver 2021-2022", "id": "/es/radio/radio-euskadi/vivir-para-ver/8220074/"}, {"title": "Vivir Para Ver 2020-2021", "id": "/es/radio/radio-euskadi/vivir-para-ver/7466463/"}, {"title": "Vivir Para Ver 2019-2020", "id": "/es/radio/radio-euskadi/vivir-para-ver/6631054/"}, {"title": "Vivir Para Ver 2018-2019", "id": "/es/radio/radio-euskadi/vivir-para-ver/5769525/"}, {"title": "Vivir Para Ver 2016-2017", "id": "/es/radio/radio-euskadi/vivir-para-ver/4367280/"}, {"title": "Vivir Para Ver 2017-2018", "id": "/es/radio/radio-euskadi/vivir-para-ver/5049628/"}, {"title": "Zidorrean 2022-2023", "id": "/eu/irratia/euskadi-irratia/zidorrean/8912924/"}, {"title": "Zidorrean 2021-2022", "id": "/eu/irratia/euskadi-irratia/zidorrean/8281632/"}, {"title": "Zidorrean 2020-2021", "id": "/eu/irratia/euskadi-irratia/zidorrean/7467899/"}, {"title": "Zidorrean 2019-2020", "id": "/eu/irratia/euskadi-irratia/zidorrean/6637322/"}, {"title": "Zidorrean 2018-2019", "id": "/eu/irratia/euskadi-irratia/zidorrean/5834427/"}, {"title": "Zidorrean 2017-2018", "id": "/eu/irratia/euskadi-irratia/zidorrean/5050374/"}, {"title": "Zidorrean 2016-2017", "id": "/eu/irratia/euskadi-irratia/zidorrean/4343219/"}, {"title": "Zidorrean 2015-2016", "id": "/eu/irratia/euskadi-irratia/zidorrean/3445698/"}, {"title": "Zidorrean 2014-2015", "id": "/eu/irratia/euskadi-irratia/zidorrean/3383334/"}, {"title": "8 d\u00edas 7 noches 2022-2023", "id": "/es/radio/radio-euskadi/8-dias-7-noches/8915140/"}, {"title": "8 d\u00edas 7 noches 2021-2022", "id": "/es/radio/radio-euskadi/8-dias-7-noches/8299858/"}] -------------------------------------------------------------------------------- /rebuild_cache.py: -------------------------------------------------------------------------------- 1 | from eitbapi.utils import _get_tv_program_data 2 | from eitbapi.utils import _get_radio_program_data 3 | 4 | import json 5 | import os 6 | 7 | 8 | def rebuild_cache(): 9 | cache_file = os.path.join(os.path.dirname(__file__), "cache.json") 10 | data = _get_tv_program_data() 11 | with open(cache_file, "w") as fp: 12 | json.dump(data, fp) 13 | 14 | cache_file = os.path.join(os.path.dirname(__file__), "radio-cache.json") 15 | data = _get_radio_program_data() 16 | with open(cache_file, "w") as fp: 17 | json.dump(data, fp) 18 | 19 | 20 | if __name__ == "__main__": 21 | rebuild_cache() 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.9.3 2 | bs4==0.0.1 3 | certifi==2020.12.5 4 | Chameleon==3.9.0 5 | chardet==4.0.0 6 | hupper==1.10.2 7 | idna==2.10 8 | Paste==3.5.0 9 | PasteDeploy==2.1.1 10 | plaster==1.0 11 | plaster-pastedeploy==0.7 12 | pyramid==1.10.8 13 | pyramid-chameleon==0.3 14 | pytz==2021.1 15 | requests==2.25.1 16 | six==1.15.0 17 | soupsieve==2.2.1 18 | translationstring==1.4 19 | urllib3==1.26.5 20 | venusian==3.0.0 21 | waitress==2.1.2 22 | WebOb==1.8.7 23 | WebTest==2.0.35 24 | youtube-dl==2021.4.17 25 | zope.deprecation==4.4.0 26 | zope.interface==5.4.0 27 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | python setup.py develop 4 | python runapp.py 5 | -------------------------------------------------------------------------------- /runapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from paste.deploy import loadapp 4 | from waitress import serve 5 | 6 | if __name__ == "__main__": 7 | port = int(os.environ.get("PORT", 5000)) 8 | wsgi_app = loadapp("config:production.ini", relative_to=".") 9 | serve(wsgi_app, host='0.0.0.0', port=port) 10 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.15 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | match = ^test 3 | nocapture = 1 4 | cover-package = eitbapi 5 | with-coverage = 1 6 | cover-erase = 1 7 | 8 | [compile_catalog] 9 | directory = eitbapi/locale 10 | domain = eitbapi 11 | statistics = true 12 | 13 | [extract_messages] 14 | add_comments = TRANSLATORS: 15 | output_file = eitbapi/locale/eitbapi.pot 16 | width = 80 17 | 18 | [init_catalog] 19 | domain = eitbapi 20 | input_file = eitbapi/locale/eitbapi.pot 21 | output_dir = eitbapi/locale 22 | 23 | [update_catalog] 24 | domain = eitbapi 25 | input_file = eitbapi/locale/eitbapi.pot 26 | output_dir = eitbapi/locale 27 | previous = true 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup, find_packages 4 | 5 | here = os.path.abspath(os.path.dirname(__file__)) 6 | with open(os.path.join(here, 'README.rst')) as f: 7 | README = f.read() 8 | with open(os.path.join(here, 'CHANGES.txt')) as f: 9 | CHANGES = f.read() 10 | 11 | tests_requires = [ 12 | 'WebTest >= 1.3.1', 13 | 'pytest', 14 | 'pytest-cov', 15 | ] 16 | 17 | requires = [ 18 | "pyramid", 19 | "Paste", 20 | "youtube-dl", 21 | "requests", 22 | "pytz", 23 | "webtest", 24 | "pyramid_chameleon", 25 | ] 26 | 27 | setup(name='eitbapi', 28 | version='1.0', 29 | description='eitbapi', 30 | long_description=README + '\n\n' + CHANGES, 31 | classifiers=[ 32 | "Programming Language :: Python", 33 | "Framework :: Pyramid", 34 | "Topic :: Internet :: WWW/HTTP", 35 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", 36 | ], 37 | author='Mikel Larreategi', 38 | author_email='larreategi@eibar.org', 39 | url='https://github.com/erral/eitbapi', 40 | license='GPLv2', 41 | keywords='web pyramid pylons eitb video api tv', 42 | packages=find_packages(), 43 | include_package_data=True, 44 | zip_safe=False, 45 | tests_require=tests_requires, 46 | extras_require={'testing': tests_requires}, 47 | install_requires=requires, 48 | test_suite="eitbapi", 49 | entry_points="""\ 50 | [paste.app_factory] 51 | main = eitbapi:main 52 | """, 53 | ) 54 | --------------------------------------------------------------------------------