Compare commits

..
Author SHA1 Message Date
Jonah Brüchert 95ea5da248 WIP: Display thumnnail in player 2021-03-11 16:46:33 +01:00
106 changed files with 975 additions and 13711 deletions

No files matched your search

-8
View File
@@ -1,9 +1 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
# SPDX-FileCopyrightText: 2021 Bhushan Shah <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*.kdev4
CMakeLists.txt.user
flatpak/flatpak-builder-tools/
build/
-6
View File
@@ -1,6 +0,0 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
include:
- 'https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml'
-25
View File
@@ -1,25 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: AudioTube
Upstream-Contact: Jonah Brüchert <[email protected]>
Source: https://invent.kde.org/plasma-mobile/audiotube
# Sample paragraph, commented out:
#
# Files: ...
# Copyright: $YEAR $NAME <$CONTACT>
# License: ...
# Comment: ...
Files: README.md
Copyright: 2021 Jonah Brüchert <[email protected]>
License: CC0-1.0
Files: org.kde.audiotube.desktop
org.kde.audiotube.json
Copyright: 2021 Jonah Brüchert <[email protected]>
License: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
Files: logo.png
org.kde.audiotube.svg
Copyright: 2021 Mathis Brüchert <[email protected]>
License: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
+7 -21
View File
@@ -1,23 +1,18 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: BSD-2-Clause
cmake_minimum_required(VERSION 3.16.0)
cmake_minimum_required(VERSION 3.14)
project(audiotube)
include(FeatureSummary)
set(PROJECT_VERSION "22.11")
set(QT5_MIN_VERSION 5.15)
set(KF5_MIN_VERSION 5.81)
set(QT5_MIN_VERSION 5.12)
set(KF5_MIN_VERSION 5.65)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
include(KDEInstallDirs)
include(KDECMakeSettings)
@@ -26,20 +21,11 @@ include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMPoQmTools)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX AUDIOTUBE
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/version.h
)
kde_enable_exceptions()
find_package(Qt${QT_MAJOR_VERSION} ${QT5_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Sql Widgets)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n CoreAddons Crash)
find_package(Qt5 ${QT5_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n)
find_package(pybind11 REQUIRED)
find_package(Ytdlp REQUIRED RUNTIME)
find_package(YTMusicAPI REQUIRED RUNTIME)
ki18n_install(po)
add_subdirectory(src)
-22
View File
@@ -1,22 +0,0 @@
Copyright (c) <year> <owner>. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-119
View File
@@ -1,119 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES
NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE
AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE
OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS
LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION
OR WORKS PROVIDED HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer exclusive
Copyright and Related Rights (defined below) upon the creator and subsequent
owner(s) (each and all, an "owner") of an original work of authorship and/or
a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later claims
of infringement build upon, modify, incorporate in other works, reuse and
redistribute as freely as possible in any form whatsoever and for any purposes,
including without limitation commercial purposes. These owners may contribute
to the Commons to promote the ideal of a free culture and the further production
of creative, cultural and scientific works, or to gain reputation or greater
distribution for their Work in part through the use and efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with
a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or
her Copyright and Related Rights in the Work and the meaning and intended
legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be protected
by copyright and related or neighboring rights ("Copyright and Related Rights").
Copyright and Related Rights include, but are not limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work, subject
to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal protection
of databases, and under any national implementation thereof, including any
amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time extensions),
(iii) in any current or future medium and for any number of copies, and (iv)
for any purpose whatsoever, including without limitation commercial, advertising
or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the
benefit of each member of the public at large and to the detriment of Affirmer's
heirs and successors, fully intending that such Waiver shall not be subject
to revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account Affirmer's
express Statement of Purpose. In addition, to the extent the Waiver is so
judged Affirmer hereby grants to each affected person a royalty-free, non
transferable, non sublicensable, non exclusive, irrevocable and unconditional
license to exercise Affirmer's Copyright and Related Rights in the Work (i)
in all territories worldwide, (ii) for the maximum duration provided by applicable
law or treaty (including future time extensions), (iii) in any current or
future medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional purposes
(the "License"). The License shall be deemed effective as of the date CC0
was applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder of
the License, and in such case Affirmer hereby affirms that he or she will
not (i) exercise any of his or her remaining Copyright and Related Rights
in the Work or (ii) assert any associated claims and causes of action with
respect to the Work, in either case contrary to Affirmer's express Statement
of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered,
licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or other
defects, accuracy, or the present or absence of errors, whether or not discoverable,
all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims
responsibility for obtaining any necessary consents, permissions or other
rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a party
to this document and has no duty or obligation with respect to this CC0 or
use of the Work.
-117
View File
@@ -1,117 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
-232
View File
@@ -1,232 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for software and other kinds of works.
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS
0. Definitions.
“This License” refers to version 3 of the GNU General Public License.
“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
A “covered work” means either the unmodified Program or a work based on the Program.
To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
1. Source Code.
The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
7. Additional Terms.
“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
11. Patents.
A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.
-12
View File
@@ -1,12 +0,0 @@
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
+2 -14
View File
@@ -2,20 +2,8 @@
AudioTube is a client for YouTube Music.
<a href='https://flathub.org/apps/details/org.kde.audiotube'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
## Links
* Project page: https://invent.kde.org/plasma-mobile/audiotube
* File issues: https://bugs.kde.org/describecomponents.cgi?product=audiotube
# Dependencies
It depends on Qt, Kirigami, KI18n, KCoreAddons and KCrash in addition to a few python packages:
It depends on Qt and Kirigami in addition to a few python packages:
* ytmusicapi
* yt_dlp
YouTube uses webp images for the cover art, so you will need to install qtimageformats.
# Troubleshooting
AudioTube only needs Python 3. If cmake still selects Python 2, you can pass `-DPYTHON_EXECUTABLE=/usr/bin/python3` to correct that.
* youtube_dl
-11
View File
@@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: BSD-2-Clause
find_package(Python3 REQUIRED COMPONENTS Interpreter)
execute_process(COMMAND ${Python3_EXECUTABLE} -c "import ytmusicapi" RESULT_VARIABLE YTMUSICAPI_CHECK_RESULT)
if (${YTMUSICAPI_CHECK_RESULT} EQUAL 0)
set(YTMusicAPI_FOUND TRUE)
endif()
-11
View File
@@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: BSD-2-Clause
find_package(Python3 REQUIRED COMPONENTS Interpreter)
execute_process(COMMAND ${Python3_EXECUTABLE} -c "import yt_dlp" RESULT_VARIABLE YTDLP_CHECK_RESULT)
if (${YTDLP_CHECK_RESULT} EQUAL 0)
set(Ytdlp_FOUND TRUE)
endif()
-39
View File
@@ -1,39 +0,0 @@
{
"name": "python3-yt-dlp",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"yt-dlp\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/2a/18/70c32fe9357f3eea18598b23aa9ed29b1711c3001835f7cf99a9818985d0/Brotli-1.0.9.zip",
"sha256": "4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630e/certifi-2022.9.24-py3-none-any.whl",
"sha256": "90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/03/ee/114d7016d2e34f341e212fefb5e7bd87785077ebcfff0ad23a497c70eea1/mutagen-1.46.0-py3-none-any.whl",
"sha256": "8af0728aa2d5c3ee5a727e28d0627966641fddfe804c23eabb5926a4d770aed5"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/52/0d/6cc95a83f6961a1ca041798d222240890af79b381e97eda3b9b538dba16f/pycryptodomex-3.15.0.tar.gz",
"sha256": "7341f1bb2dadb0d1a0047f34c3a58208a92423cdbd3244d998e4b28df5eac0ed"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/85/dc/549a807a53c13fd4a8dac286f117a7a71260defea9ec0c05d6027f2ae273/websockets-10.4.tar.gz",
"sha256": "eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/2b/8c/6382d335662361fe06bea1ca96a189982cc050c2f9946dcd320b8c26a833/yt_dlp-2022.10.4-py2.py3-none-any.whl",
"sha256": "cc21d91048b275bda7018bdc3103374882261ccf29720206d61b10e168c8b719"
}
]
}
-3
View File
@@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-39
View File
@@ -1,39 +0,0 @@
{
"name": "python3-ytmusicapi",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"ytmusicapi\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630e/certifi-2022.9.24-py3-none-any.whl",
"sha256": "90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/db/51/a507c856293ab05cdc1db77ff4bc1268ddd39f29e7dc4919aa497f0adbec/charset_normalizer-2.1.1-py3-none-any.whl",
"sha256": "83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl",
"sha256": "90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl",
"sha256": "8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/6f/de/5be2e3eed8426f871b170663333a0f627fc2924cc386cd41be065e7ea870/urllib3-1.26.12-py2.py3-none-any.whl",
"sha256": "b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/74/5f/929784007a6777272b3bd51dc7a13e39d5192d64b3831535e83776cd825d/ytmusicapi-0.24.0-py3-none-any.whl",
"sha256": "ac25baec1314274030e422c841932d94119394f54c7e70c64a2be1f26be69c2b"
}
]
}
-3
View File
@@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
set -e
export GIT_CLONE_ARGS="--depth 1 --single-branch"
export FLATPAK_DIR="$(readlink -f $(dirname $0))"
cd ${FLATPAK_DIR}
if [ ! -d flatpak-builder-tools ]; then
git clone ${GIT_CLONE_ARGS} https://github.com/flatpak/flatpak-builder-tools
else
git -C flatpak-builder-tools pull
fi
./flatpak-builder-tools/pip/flatpak-pip-generator yt-dlp
./flatpak-builder-tools/pip/flatpak-pip-generator ytmusicapi
+12 -184
View File
@@ -1,190 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
SPDX-License-Identifier: CC0-1.0
-->
<component type="desktop-application">
<id>org.kde.audiotube</id>
<name>AudioTube</name>
<name xml:lang="ar">تيوب الصوتي</name>
<name xml:lang="az">AudioTube</name>
<name xml:lang="ca">AudioTube</name>
<name xml:lang="ca-valencia">AudioTube</name>
<name xml:lang="cs">AudioTube</name>
<name xml:lang="de">AudioTube</name>
<name xml:lang="en-GB">AudioTube</name>
<name xml:lang="es">AudioTube</name>
<name xml:lang="eu">AudioTube</name>
<name xml:lang="fi">AudioTube</name>
<name xml:lang="fr">AudioTube</name>
<name xml:lang="hi">ओडियो-ट्यूब</name>
<name xml:lang="id">AudioTube</name>
<name xml:lang="it">AudioTube</name>
<name xml:lang="ka">AudioTube</name>
<name xml:lang="ko">AudioTube</name>
<name xml:lang="nl">AudioTube</name>
<name xml:lang="nn">AudioTube</name>
<name xml:lang="pa">ਆਡੀਓ-ਟਿਊਬ</name>
<name xml:lang="pl">AudioTube</name>
<name xml:lang="pt">AudioTube</name>
<name xml:lang="pt-BR">AudioTube</name>
<name xml:lang="ro">AudioTube</name>
<name xml:lang="sk">AudioTube</name>
<name xml:lang="sl">AudioTube</name>
<name xml:lang="sv">AudioTube</name>
<name xml:lang="tr">AudioTube</name>
<name xml:lang="uk">AudioTube</name>
<name xml:lang="x-test">xxAudioTubexx</name>
<name xml:lang="zh-CN">AudioTube</name>
<summary>Convergent YouTube Music client</summary>
<summary xml:lang="ar">عميل موسيقى يوتيوب متعدد المنصات</summary>
<summary xml:lang="az">Birləşmiş YouTube Musiqi müştərisi</summary>
<summary xml:lang="ca">Client convergent de música de YouTube</summary>
<summary xml:lang="ca-valencia">Client convergent de música de YouTube</summary>
<summary xml:lang="de">Konvergentes YouTube-Musikprogramm</summary>
<summary xml:lang="en-GB">Convergent YouTube Music client</summary>
<summary xml:lang="es">Cliente de música de YouTube convergente</summary>
<summary xml:lang="eu">YouTube Music bezero konbergentea</summary>
<summary xml:lang="fi">Mukautuva YouTube Music -asiakas</summary>
<summary xml:lang="fr">Client convergent pour musique sur YouTube</summary>
<summary xml:lang="hi">अभिसृत यूट्यूब संगीत क्लाएंट</summary>
<summary xml:lang="id">Klien YouTube Music konvergen</summary>
<summary xml:lang="it">Client YouTube Music convergente</summary>
<summary xml:lang="ka">YouTube Music -ის კონვერგენტული კლიენტი</summary>
<summary xml:lang="ko">올인원 YouTube Music 클라이언트</summary>
<summary xml:lang="nl">Convergente muziekclient van YouTube</summary>
<summary xml:lang="nn">YouTube Music-klient, tilpassa ulike brukarflater</summary>
<summary xml:lang="pl">Adaptacyjny program do YouTube Music</summary>
<summary xml:lang="pt">Cliente de música do YouTube convergente</summary>
<summary xml:lang="pt-BR">Um cliente convergente para o YouTube Music</summary>
<summary xml:lang="ro">Client convergent de muzică pentru YouTube</summary>
<summary xml:lang="sl">Konvergentni odjemalec glasbe YouTube</summary>
<summary xml:lang="sv">Konvergent YouTube-musikprogram</summary>
<summary xml:lang="tr">Yakınsak YouTube Müzik istemcisi</summary>
<summary xml:lang="uk">Зручний музичний клієнт YouTube</summary>
<summary xml:lang="x-test">xxConvergent YouTube Music clientxx</summary>
<summary xml:lang="zh-CN">桌面和移动设备通用 YouTube Music 客户端</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+</project_license>
<developer_name>KDE Community</developer_name>
<developer_name xml:lang="ar">مجتمع كدي</developer_name>
<developer_name xml:lang="az">KDE İcması</developer_name>
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
<developer_name xml:lang="cs">Komunita KDE</developer_name>
<developer_name xml:lang="de">KDE-Gemeinschaft</developer_name>
<developer_name xml:lang="en-GB">KDE Community</developer_name>
<developer_name xml:lang="es">Comunidad KDE</developer_name>
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
<developer_name xml:lang="fr">Communauté de KDE</developer_name>
<developer_name xml:lang="hi">केडीई समुदाय</developer_name>
<developer_name xml:lang="id">Komunitas KDE</developer_name>
<developer_name xml:lang="it">Comunità KDE</developer_name>
<developer_name xml:lang="ka">KDE-ის საზოგადოება</developer_name>
<developer_name xml:lang="ko">KDE 커뮤니티</developer_name>
<developer_name xml:lang="nl">KDE-gemeenschap</developer_name>
<developer_name xml:lang="nn">KDE-fellesskapet</developer_name>
<developer_name xml:lang="pa">ਕੇਡੀਈ ਕਮਿਊਨਟੀ</developer_name>
<developer_name xml:lang="pl">Społeczność KDE</developer_name>
<developer_name xml:lang="pt">Comunidade do KDE</developer_name>
<developer_name xml:lang="pt-BR">A Comunidade KDE</developer_name>
<developer_name xml:lang="ro">Comunitatea KDE</developer_name>
<developer_name xml:lang="sk">KDE komunita</developer_name>
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
<developer_name xml:lang="x-test">xxKDE Communityxx</developer_name>
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
<content_rating type="oars-1.0"/>
<url type="homepage">https://apps.kde.org/en/audiotube</url>
<releases>
<release version="22.11" date="2022-11-30">
<url>https://plasma-mobile.org/2022/11/30/plasma-mobile-gear-22-11/</url>
</release>
<release version="22.09" date="2022-09-27">
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
</release>
<release version="22.06" date="2022-06-24">
<url>https://www.plasma-mobile.org/2022/06/28/plasma-mobile-gear-22-06/</url>
<description>
<ul>
<li>Add library page which shows favourites and most played songs</li>
<li>Improve layout of the search popup</li>
<li>General performance improvements including startup</li>
</ul>
</description>
</release>
<release version="22.04" date="2022-04-26">
<url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url>
<description>
<ul>
<li>Update the version of ytmusicapi</li>
<li>General code-refactoring and translation updates</li>
</ul>
</description>
</release>
<release version="22.02" date="2022-02-09">
<url>https://www.plasma-mobile.org/2022/02/09/plasma-mobile-gear-22-02/</url>
<description>
<ul>
<li>Update the version of ytmusicapi</li>
</ul>
</description>
</release>
<release version="21.12" date="2021-12-07">
<url>https://www.plasma-mobile.org/2021/12/07/plasma-mobile-gear-21-12/</url>
</release>
<release version="21.08" date="2021-08-31">
<url>https://www.plasma-mobile.org/2021/08/31/plasma-mobile-gear-21-08/</url>
</release>
</releases>
<launchable type="desktop-id">org.kde.audiotube.desktop</launchable>
<screenshots>
<screenshot>
<image>https://cdn.kde.org/screenshots/audiotube/library.png</image>
</screenshot>
<screenshot>
<image>https://cdn.kde.org/screenshots/audiotube/mobile_playlist.png</image>
</screenshot>
<screenshot>
<image>https://cdn.kde.org/screenshots/audiotube/desktop.png</image>
</screenshot>
</screenshots>
<recommends>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</recommends>
<name>Kirigami Application</name>
<summary>A short summary describing what this software is about</summary>
<metadata_license>A permissive license for this metadata, e.g. "FSFAP"</metadata_license>
<project_license>The license of this software as SPDX string, e.g. "GPL-3+"</project_license>
<developer_name>The software vendor name, e.g. "ACME Corporation"</developer_name>
<description>
<p>AudioTube can search YouTube Music, list albums and artists, play automatically generated playlists, albums and allows to put your own playlist together.</p>
<p xml:lang="ar">يمكن لتيوب الصوتي البحث في موسيقى يوتيوب، وعرض الألبومات والفنانين، وتشغيل القوائم والألبومات المولدة تلقائيًا، ويسمح لك بوضع قائمة التشغيل الخاصة بك معًا.</p>
<p xml:lang="az">AudioTube YouTube Musiqi, albomların və ifaçıların siyahısı üzrə axtarış etməyə, yaradılmış mahnı siyahısı və albomları avtomatik oxutmağa və öz mahnı siyahınızı yaratmağa imkan verir.</p>
<p xml:lang="ca">L'AudioTube pot cercar música a YouTube, llistar àlbums i artistes, reproduir llistes de reproducció generades automàticament, àlbums i permet crear la vostra pròpia llista de reproducció.</p>
<p xml:lang="ca-valencia">L'AudioTube pot buscar música a YouTube, llistar àlbums i artistes, reproduir llistes de reproducció generades automàticament, àlbums i permet crear la vostra pròpia llista de reproducció.</p>
<p xml:lang="de">AudioTube kann Musik auf YouTube durchsuchen, Alben und Interpreten auflisten, automatisch generierte Wiedergabelisten und Alben abspielen und erlaubt es, eine eigene Wiedergabeliste zusammenzustellen.</p>
<p xml:lang="en-GB">AudioTube can search YouTube Music, list albums and artists, play automatically generated playlists, albums and allows to put your own playlist together.</p>
<p xml:lang="es">AudioTube puede buscar música de YouTube, listar álbumes y artistas, reproducir las listas de reproducción generadas, álbumes y le permite crear su propia lista de reproducción.</p>
<p xml:lang="eu">AudioTube-k YouTube Music-en bilatu dezake, albumak eta artistak zerrendatu, automatikoki sortutako jotze-zerrendak eta albumak jo, eta zure jotze-zerrenda osatzeko aukera eskaintzen dizu.</p>
<p xml:lang="fi">AudioTube osaa etsiä YouTube Music -haun, albumien ja artistien luettelun, luotujen soittolistojen automaattisen toiston ja omien soittolistojen kokoamisen.</p>
<p xml:lang="fr">AudioTube peut effectuer des recherches de musiques sur YouTube Music, établir une liste d'albums et d'artistes, lire des listes de lecture générées automatiquement, des albums et permet de composer sa propre liste de lecture.</p>
<p xml:lang="hi">ऑडियोट्यूब यूट्यूब संगीत खोज सकता है, एल्बम और कलाकारों की सूची बना सकता है, स्वचालित रूप से उत्पन्न की गई गीतसूची, एल्बम बजा सकता है और आपकी अपनी खुद की प्लेलिस्ट को एक साथ रखने में मदद करता है।</p>
<p xml:lang="id">AudioTube dapat menelusuri YouTube Music, daftarkan album dan artis, putar daftar putar dan yang dibuat secara otomatis dan album, dan memungkinkan Anda untuk membuat daftar putar Anda sendiri.</p>
<p xml:lang="it">AudioTube può eseguire ricerche su YouTube Music, elencare album e artisti, riprodurre scalette e album generati automaticamente e ti consente di creare la tua scaletta.</p>
<p xml:lang="ka">AudioTube -ს შეუძლია მოძებნოს მუსიკა YouTube-ზე, გაჩვენოთ ალბომებისა და შემსრულებლების სია, დაუკრას დაგენერირებული დასაკრავი სიები, ალბომები და საშუალებას გაძლევთ, თქვენი დასაკრავი სია შექმნათ.</p>
<p xml:lang="ko">AudioTube는 YouTube Music을 검색하고, 앨범과 아티스트를 표시하고, 자동 생성된 재생 목록, 앨범을 재생하고, 사용자 정의 재생 목록을 만들 수 있습니다.</p>
<p xml:lang="nl">AudioTube kan in YouTube Music zoeken, een lijst maken van albums en artiesten, automatically gegenereerde afspeellijsten, albums afspelen en biedt u het samenstellen van uw eigen afspeellijst.</p>
<p xml:lang="nn">Med AudioTube kan du søkja på YouTube Music, visa album og artistar, spela automatiske genererte spelelister og album samt laga eigne spelelister.</p>
<p xml:lang="pl">AudioTube może przeszukiwać serwis YouTube Music, wypisać albumy i wykonawców, odtwarzać samoczynnie utworzone listy odtwarzania, albumy oraz umożliwia tworzenie własnych list odtwarzania.</p>
<p xml:lang="pt">O AudioTube consegue pesquisar no YouTube Music, enumerar os álbuns e artistas, reproduzir listas geradas automaticamente, álbuns e permite-lhe reproduzir as suas próprias listas de reprodução.</p>
<p xml:lang="pt-BR">O AudioTube pode pesquisar no YouTube Music, listar álbuns e artistas, rodar listas de reprodução geradas automaticamente, álbuns inteiros, e criar a sua própria lista de reprodução.</p>
<p xml:lang="sl">AudioTube lahko išče po YouTube Music, izdela sezname albumov in avtorjev, samodejno predvaja generirane sezname, albume in sestavlja vaše sezname predvajanja.</p>
<p xml:lang="sv">AudioTube kan söka på YouTube Music, lista album och artister, spela upp automatiskt genererade spellistor, albums och låter dig sammanställa egna spellistor.</p>
<p xml:lang="tr">AudioTube, YouTube Müzik'te arama yapabilir; albümleri, sanatçıları listeler; kendiliğinden oluşturulmuş çalma listelerini, albümleri çalar ve kendi çalma listenizi oluşturmanıza izin verir.</p>
<p xml:lang="uk">AudioTube може виконувати пошук на YouTube Music, показувати списки альбомів та виконавців, відтворювати створені автоматично списки відтворення, альбоми та надає вам змогу створювати власні списки відворення.</p>
<p xml:lang="x-test">xxAudioTube can search YouTube Music, list albums and artists, play automatically generated playlists, albums and allows to put your own playlist together.xx</p>
<p xml:lang="zh-CN">AudioTube 可以搜索 YouTube Music 的内容,列出专辑和艺人,播放自动生成的播放列表,还可以自行制作播放列表。</p>
<p>Multiple paragraphs of long description, describing this software component.</p>
<p>You can also use ordered and unordered lists:</p>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
</ul>
<p>Keep in mind to XML-escape characters, and that this is not HTML markup.</p>
</description>
</component>
+3 -63
View File
@@ -1,69 +1,9 @@
[Desktop Entry]
Name=AudioTube
Name[ar]=تيوب الصوتي
Name[az]=AudioTube
Name[ca]=AudioTube
Name[ca@valencia]=AudioTube
Name[cs]=AudioTube
Name[de]=AudioTube
Name[en_GB]=AudioTube
Name[es]=AudioTube
Name[eu]=AudioTube
Name[fi]=AudioTube
Name[fr]=AudioTube
Name[hi]=ओडियो-ट्यूब
Name[id]=AudioTube
Name[it]=AudioTube
Name[ka]=AudioTube
Name[ko]=AudioTube
Name[nl]=AudioTube
Name[nn]=AudioTube
Name[pa]=ਆਡੀਓ-ਟਿਊਬ
Name[pl]=AudioTube
Name[pt]=AudioTube
Name[pt_BR]=AudioTube
Name[ro]=AudioTube
Name[sk]=AudioTube
Name[sl]=AudioTube
Name[sv]=AudioTube
Name[tr]=AudioTube
Name[uk]=AudioTube
Name[x-test]=xxAudioTubexx
Name[zh_CN]=AudioTube
Comment=YouTube Music client
Comment[ar]=عميل يوتيوب ميوزيك
Comment[az]=YouTube Musiqi müştərisi
Comment[ca]=Client de música de YouTube
Comment[ca@valencia]=Client de música de YouTube
Comment[cs]=Hudební klient YouTube
Comment[de]=YouTube-Musikprogramm
Comment[en_GB]=YouTube Music client
Comment[es]=Cliente de música de YouTube
Comment[eu]=YouTube Music bezeroa
Comment[fi]=YouTube Music -asiakas
Comment[fr]=Client de musique sur Youtube
Comment[hi]=यूट्यूब संगीत क्लाएंट
Comment[id]=Klien YouTube Music
Comment[it]=Client YouTube Music
Comment[ka]=YouTube Music -ის კლიენტი
Comment[ko]=YouTube Music 클라이언트
Comment[nl]=Muziekclient van YouTube
Comment[nn]=YouTube Music-klient
Comment[pa]=ਯੂਟਿਊਬ ਸੰਗੀਤ ਕਲਾਈਂਟ
Comment[pl]=Program do YouTube Music
Comment[pt]=Cliente do YouTube Music
Comment[pt_BR]=Cliente YouTube Music
Comment[ro]=Client de muzică pentru YouTube
Comment[sk]=Hudobný klient YouTube
Comment[sl]=Glasbeni odjemalec Youtube
Comment[sv]=YouTube-musikprogram
Comment[tr]=YouTube Müzik istemcisi
Comment[uk]=Музичний клієнт YouTube
Comment[x-test]=xxYouTube Music clientxx
Comment[zh_CN]=YouTube Music 客户端
Comment=youtube_music Kirigami Application
Version=1.0
Exec=audiotube
Icon=org.kde.audiotube
Icon=applications-development
Type=Application
Terminal=false
Categories=Qt;KDE;AudioVideo;Player;
Categories=Qt;KDE;
-42
View File
@@ -1,42 +0,0 @@
{
"id": "org.kde.audiotube",
"runtime": "org.kde.Platform",
"runtime-version": "5.15-21.08",
"sdk": "org.kde.Sdk",
"command": "audiotube",
"tags": ["nightly"],
"desktop-file-name-suffix": " (Nightly)",
"finish-args": [
"--share=ipc",
"--share=network",
"--socket=x11",
"--socket=wayland",
"--socket=pulseaudio",
"--device=dri"
],
"separate-locales": false,
"cleanup": [
"/include/",
"/share/cmake/"
],
"modules": [
"flatpak/python3-yt-dlp.json",
"flatpak/python3-ytmusicapi.json",
{
"name": "pybind11",
"config-opts": ["-DPYBIND11_FINDPYTHON=ON", "-DPYBIND11_TEST=OFF"],
"buildsystem": "cmake-ninja",
"builddir": true,
"sources": [ { "type": "git", "url": "https://github.com/pybind/pybind11", "tag": "v2.9.1" } ]
},
{
"name": "org.kde.audiotube",
"config-opts": ["-DPYBIND11_FINDPYTHON=ON"],
"buildsystem": "cmake-ninja",
"builddir": true,
"sources": [ { "type": "dir", "path": ".", "skip": [".git"] } ]
}
]
}
-244
View File
@@ -1,244 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Zayed Al-Saidi <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-07 19:04+0400\n"
"Last-Translator: Zayed Al-Saidi <[email protected]>\n"
"Language-Team: ar\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Generator: Lokalize 21.12.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "زايد السعيدي"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "شغّل"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "اخلط"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "افتح في المتصفّح"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "راديو"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "الفنّانون"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "تشغيل الفيديو غير مدعوم حتى الآن. هل تريد تشغيل صوت فقط من \"%1\"؟"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "الألبومات"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "فردي"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "أغاني"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "الفيديوهات"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "حسنًا"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "ألغِ"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "المفضلة"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "أظهر الكلّ"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "الأغاني المفضّلة"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "الأكثر تشغيلا"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "الأغاني المشغلة"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "عُد"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "ابحث"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "تيوب الصوتي"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "المشغل الآن"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "صُفّ"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "الكلمات"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "أزل المسار"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "لا يوجد وسائط تعمل"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "قائمة غير معروفة من الأغاني"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "لا يوجد أي أغنية بعد"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "قوائم التّشغيل"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "الفنّانون"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "قوائم التّشغيل"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "مجهول"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "اعثر على موسيقى على موسيقى يوتيوب"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "شغل التالي"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "المفضلة"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "المفضلة"
#~ msgid "Add to Playlist"
#~ msgstr "أضف إلى قائمة التّشغيل"
#~ msgid "Play next"
#~ msgstr "شغل التالي"
#~ msgid "Clear"
#~ msgstr "امسح"
#~ msgid "Pause"
#~ msgstr "ألبث"
#~ msgid "Expand"
#~ msgstr "وسع"
-245
View File
@@ -1,245 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Kheyyam Gojayev <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-06-19 12:16+0400\n"
"Last-Translator: Kheyyam <[email protected]>\n"
"Language-Team: Azerbaijani <[email protected]>\n"
"Language: az\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.04.2\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Xəyyam Qocayev"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Başladın"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Qarışıq"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "İfaçılar"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Video göstərilməsi hələlik dəstəklənmir. Siz yalnız \"%1\" videosunun səsini "
"oynatmaq istəyirsiniz?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albomlar"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Musiqilər"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Mahnılar"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videolar"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OLDU"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "İmtina"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Süçilmişlər"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Hamısı göstərilsin"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Seçilmiş mahnılar"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Daha çox dinlənilən"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Oxudulan mahnılar"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Geriyə"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Axtarın"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "İndi səslənir"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Növbə"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Mahnını silin"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Media faylı oynadılmır"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Naməlum mahnı siyahısı"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Hələlik burada mahnılar yoxdur"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Mahnı siyahıları"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "İfaçılar"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Mahnı siyahıları"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Bilinməyən"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Musiqini YouTube Musiqi-də tapın"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Sonrakını oxudun"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Süçilmişlər"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Süçilmişlər"
#~ msgid "Add to Playlist"
#~ msgstr "Mahnı siyahısına əlavə edin"
#~ msgid "Play next"
#~ msgstr "Sonrakını başladın"
#~ msgid "Clear"
#~ msgstr "Təmizləyin"
#~ msgid "Pause"
#~ msgstr "Fasilə"
#~ msgid "Expand"
#~ msgstr "Açmaq"
-229
View File
@@ -1,229 +0,0 @@
# Translation of audiotube.po to Catalan
# Copyright (C) 2021-2022 This_file_is_part_of_KDE
# This file is distributed under the license LGPL version 2.1 or
# version 3 or later versions approved by the membership of KDE e.V.
#
# Antoni Bella Pérez <[email protected]>, 2021.
# Josep M. Ferrer <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 08:58+0100\n"
"Last-Translator: Josep M. Ferrer <[email protected]>\n"
"Language-Team: Catalan <[email protected]>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 20.12.0\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Antoni Bella"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Reprodueix"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Barreja"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Obre en el navegador"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Àlbum • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Més"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Ràdio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artista"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Encara no s'admet la reproducció de vídeo. Voleu reproduir només l'àudio de "
"«%1»?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Àlbums"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Individuals"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Cançons"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vídeos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "D'acord"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancel·la"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Preferides"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostra-ho tot"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Cançons preferides"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Les més reproduïdes"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Cançons reproduïdes"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Enrere"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Cerca"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Ara s'està reproduint"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Cua"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Lletra"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Elimina la pista"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "No s'està reproduint cap suport"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Llista desconeguda de cançons"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Encara no hi ha cap cançó"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Llista de reproducció"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistes"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Llistes de reproducció"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Desconeguda"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Cerca a la Música de Youtube"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Reprodueix el següent"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Afegeix a la cua"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Elimina de les preferides"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Afegeix a les preferides"
-229
View File
@@ -1,229 +0,0 @@
# Translation of audiotube.po to Catalan (Valencian)
# Copyright (C) 2021-2022 This_file_is_part_of_KDE
# This file is distributed under the license LGPL version 2.1 or
# version 3 or later versions approved by the membership of KDE e.V.
#
# Antoni Bella Pérez <[email protected]>, 2021.
# Josep M. Ferrer <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 08:58+0100\n"
"Last-Translator: Josep M. Ferrer <[email protected]>\n"
"Language-Team: Catalan <[email protected]>\n"
"Language: ca@valencia\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 20.12.0\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Antoni Bella"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Reproduïx"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Barreja"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Obri en el navegador"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Àlbum • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Més"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Ràdio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artista"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Encara no s'admet la reproducció de vídeo. Voleu reproduir només l'àudio de "
"«%1»?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Àlbums"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Individuals"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Cançons"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vídeos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "D'acord"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancel·la"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Preferides"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostra-ho tot"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Cançons preferides"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Les més reproduïdes"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Cançons reproduïdes"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Arrere"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Busca"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Ara s'està reproduint"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Cua"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Lletra"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Elimina la pista"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "No s'està reproduint cap suport"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Llista desconeguda de cançons"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Encara no hi ha cap cançó"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Llista de reproducció"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistes"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Llistes de reproducció"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Desconeguda"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Busca en la Música de Youtube"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Reproduïx el següent"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Afig a la cua"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Elimina de les preferides"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Afig a les preferides"
-236
View File
@@ -1,236 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Vit Pelcak <[email protected]>, 2021, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-06-28 13:58+0200\n"
"Last-Translator: Vit Pelcak <[email protected]>\n"
"Language-Team: Czech <[email protected]>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Lokalize 22.04.2\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Vít Pelčák"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Přehrát"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Zamíchat"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Otevřít v prohlížeči"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Rádio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Umělci"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Přehrávání videa ještě není podporováno. Chcete z \"%1\" přehrávat pouze "
"zvuk?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Alba"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Samotné"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Písně"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videa"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Zrušit"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Oblíbené"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Zobrazit vše"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Oblíbené skladby"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Nejčastěji hrané"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Přehrané skladby"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Zpět"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Hledat"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Nyní přehrávám"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Fronta"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Odstranit skladbu"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Nepřehrává se žádné médium"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Neznámý seznam skladeb"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Zatím zde nejsou žádné skladby"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Seznamy skladeb"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Umělci"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Seznamy skladeb"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Neznámý"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Najít hudbu na Hudba YouTube"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Přehrát následující"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Oblíbené"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Oblíbené"
#~ msgid "Add to Playlist"
#~ msgstr "Přidat do seznamu skladeb"
#~ msgid "Play next"
#~ msgstr "Přehrát následující"
-250
View File
@@ -1,250 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Burkhard Lück <[email protected]>, 2021.
# Frederik Schwarzer <[email protected]>, 2021, 2022.
# Frank Steinmetzger <[email protected]>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-10 20:16+0100\n"
"Last-Translator: Frank Steinmetzger <[email protected]>\n"
"Language-Team: German <[email protected]>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 22.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Deutsches KDE-Übersetzerteam"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Wiedergabe"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Zufällig"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "In Browser öffnen"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Interpreten"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Die Video-Wiedergabe wird noch nicht unterstützt. Möchten Sie nur die "
"Audiospur von „%1“ abspielen?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Alben"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singles"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Songs"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Abbrechen"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoriten"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Alle anzeigen"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Lieblingsstücke"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Am meisten abgespielt"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Abgespielte Stücke"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Zurück"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Suchen"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Aktueller Titel"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Warteschlange"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Stück entfernen"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Es werden keine Medien wiedergegeben"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Unbekannte Liste von Liedern"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Bisher keine Lieder hier"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Wiedergabelisten"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Interpreten"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Wiedergabelisten"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Unbekannt"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Musik auf YouTube suchen"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Nächste Wiedergabe"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favoriten"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favoriten"
#~ msgid "Add to Playlist"
#~ msgstr "Zur Wiedergabeliste hinzufügen"
#~ msgid "Play next"
#~ msgstr "Nächste Wiedergabe"
#~ msgid "Clear"
#~ msgstr "Löschen"
#~ msgid "Pause"
#~ msgstr "Pause"
#~ msgid "Expand"
#~ msgstr "Erweitern"
#~ msgid "Play only Audio"
#~ msgstr "Nur Audio abspielen"
-248
View File
@@ -1,248 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Steve Allewell <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-26 12:33+0000\n"
"Last-Translator: Steve Allewell <[email protected]>\n"
"Language-Team: British English <[email protected]>\n"
"Language: en_GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 21.12.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Steve Allewell"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Play"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Shuffle"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Open in Browser"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artists"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albums"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singles"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Songs"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancel"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favourites"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Show All"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Favourite Songs"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Most played"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Played Songs"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Back"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Search"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Now Playing"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Queue"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Lyrics"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Remove Track"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "No media playing"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Unknown list of songs"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "No songs here yet"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Playlists"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artists"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Playlists"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Unknown"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Find music on YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Play Next"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favourites"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favourites"
#~ msgid "Add to Playlist"
#~ msgstr "Add to Playlist"
#~ msgid "Play next"
#~ msgstr "Play next"
#~ msgid "Clear"
#~ msgstr "Clear"
#~ msgid "Pause"
#~ msgstr "Pause"
#~ msgid "Expand"
#~ msgstr "Expand"
#~ msgid "Play only Audio"
#~ msgstr "Play only Audio"
-246
View File
@@ -1,246 +0,0 @@
# Spanish translations for audiotube.po package.
# Copyright (C) 2021 This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Automatically generated, 2021.
# Eloy Cuadra <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 05:14+0100\n"
"Last-Translator: Eloy Cuadra <[email protected]>\n"
"Language-Team: Spanish <[email protected]>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 22.11.80\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Eloy Cuadra"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Reproducir"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Desordenar"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Abrir en el navegador"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Álbum • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Más"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artista"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"La reproducción de vídeo aún no está permitida. ¿Desea reproducir solo el "
"sonido de «%1»?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Álbumes"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Sencillos"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Canciones"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vídeos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Aceptar"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancelar"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoritas"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostrar todas"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Canciones favoritas"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Más reproducidas"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Canciones reproducidas"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Volver"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Buscar"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Reproduciendo"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Cola"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Letra"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Eliminar pista"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Ningún medio en reproducción"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Lista de canciones desconocida"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Todavía no contiene canciones"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Lista de reproducción"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistas"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Listas de reproducción"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Desconocido"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Encuentre música en YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Reproducir siguiente"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Añadir a la cola"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Eliminar favorita"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Añadir favorita"
#~ msgid "Add to Playlist"
#~ msgstr "Añadir a la lista de reproducción"
#~ msgid "Play next"
#~ msgstr "Reproducir siguiente"
#~ msgid "Clear"
#~ msgstr "Borrar todo"
#~ msgid "Pause"
#~ msgstr "Pausar"
#~ msgid "Expand"
#~ msgstr "Expandir"
#~ msgid "Play only Audio"
#~ msgstr "Reproducir solo el sonido"
-247
View File
@@ -1,247 +0,0 @@
# Translation for audiotube.po to Euskara/Basque (eu).
# Copyright (C) 2021-2022 This file is copyright:
# This file is distributed under the same license as the audiotube package.
# KDE euskaratzeko proiektuko arduraduna <[email protected]>.
#
# Translators:
# Iñigo Salvador Azurmendi <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-29 05:34+0100\n"
"Last-Translator: Iñigo Salvador Azurmendi <[email protected]>\n"
"Language-Team: Basque <[email protected]>\n"
"Language: eu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Iñigo Salvador Azurmendi"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Jo"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Nahastu"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Ireki arakatzailean"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Albuma • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Gehiago"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Irratia"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artista"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Oraindik ez dago bideoa atzera jotzeko euskarririk. «%1»(r)en audioa "
"bakarrik jo nahi duzu?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albumak"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Banakoa"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Abestiak"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Bideoak"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Ados"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Utzi"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Gogokoak"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Erakutsi denak"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Gogoko abestiak"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Gehien jotakoa"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Jotako abestiak"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Atzera"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Bilatu"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Orain jotzen"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Ilara"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Hitzak"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Kendu pista"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Ez da euskarririk jotzen ari"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Abesti zerrenda ezezaguna"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Hemen oraindik ez dago abestirik"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Jotze-zerrenda"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistak"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Jotze-zerrendak"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Ezezaguna"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Aurkitu musika «Youtube Music»en"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Jo hurrengoa"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Gehitu ilaran"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Kendu gogokoa"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Gehitu gogokoa"
#~ msgid "Add to Playlist"
#~ msgstr "Gehitu jotze-zerrendara"
#~ msgid "Play next"
#~ msgstr "Jo hurrengoa"
#~ msgid "Clear"
#~ msgstr "Garbitu"
#~ msgid "Pause"
#~ msgstr "Eten"
#~ msgid "Expand"
#~ msgstr "Zabaldu"
#~ msgid "Play only Audio"
#~ msgstr "Audio bakarrik jo"
-247
View File
@@ -1,247 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Tommi Nieminen <[email protected]>, 2021, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-10-24 07:57+0300\n"
"Last-Translator: Tommi Nieminen <[email protected]>\n"
"Language-Team: Finnish <[email protected]>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 20.12.0\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Tommi Nieminen"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Toista"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Sekoita"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Avaa selaimeen"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artistit"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Videotoistoa ei vielä tueta. Haluatko toistaa videosta ”%1” vain äänen?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albumit"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singlet"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Kappaleet"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videot"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Peru"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Suosikit"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Näytä kaikki"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Suosikkikappaleet"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Soitetuimmat"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Soitetut kappaleet"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Takaisin"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Etsi"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Nyt soi"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Jono"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Poista raita"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Mitään ei toisteta"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Tuntematon kappaleluettelo"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Ei vielä kappaleita"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Soittolistat"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistit"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Soittolistat"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Tuntematon"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Etsi musiikkia Youtube Musicista"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Toista seuraava"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Suosikit"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Suosikit"
#~ msgid "Add to Playlist"
#~ msgstr "Lisää soittolistaan"
#~ msgid "Play next"
#~ msgstr "Toista seuraava"
#~ msgid "Clear"
#~ msgstr "Tyhjennä"
#~ msgid "Pause"
#~ msgstr "Tauko"
#~ msgid "Expand"
#~ msgstr "Laajenna"
#~ msgid "Play only Audio"
#~ msgstr "Toista vain ääni"
-245
View File
@@ -1,245 +0,0 @@
# Xavier Besnard <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-07 14:29+0100\n"
"Last-Translator: Xavier Besnard <[email protected]>\n"
"Language-Team: French <[email protected]>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Lokalize 22.08.2\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Xavier Besnard"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Lire"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Mélanger"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Ouvrir dans un navigateur"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artistes"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"La lecture de vidéos n'est pas encore prise en charge. Voulez-vous vraiment "
"lire uniquement l'audio de « %1 » ?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albums"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Titres uniques"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Chansons"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vidéos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Ok"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Annuler"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoris"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Tout afficher"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Titres favoris"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Les plus lus"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Titres lus"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "En arrière"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Rechercher"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "En cours de lecture"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Mettre en file d'attente"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Paroles"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Supprimer une piste"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Aucun média en lecture"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Liste inconnue de titres"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Aucun titre ici encore"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Listes de lecture"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistes"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Listes de lecture"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Inconnu"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Rechercher de la musique sur « YouTube Music »"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Lire le suivant"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favoris"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favoris"
#~ msgid "Add to Playlist"
#~ msgstr "Ajouter à la liste de lecture"
#~ msgid "Play next"
#~ msgstr "Lire le suivant"
#~ msgid "Clear"
#~ msgstr "Effacer"
#~ msgid "Pause"
#~ msgstr "Pause"
#~ msgid "Expand"
#~ msgstr "Développer"
#~ msgid "Play only Audio"
#~ msgstr "Ne lire que l'audio"
-246
View File
@@ -1,246 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Raghavendra Kamath <[email protected]>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2021-09-17 20:09+0530\n"
"Last-Translator: Raghavendra Kamath <[email protected]>\n"
"Language-Team: kde-hindi\n"
"Language: hi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
"X-Generator: Lokalize 21.08.1\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "राघवेंद्र कामत"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "बजाएँ"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "फेंटें"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "रेडियो"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "कलाकार"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"वीडियो चलाना अभी तक समर्थित नहीं है। क्या आप केवल \"%1\" का ध्वनी चलाना चाहते हैं?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "एल्बम्स"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "एक गीत"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "गीत"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "वीडियो"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr ""
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr ""
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "खोजें"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "ओडियो-ट्यूब"
#: contents/ui/MaximizedPlayerPage.qml:105
#, fuzzy, kde-format
#| msgid "No media playing"
msgid "Now Playing"
msgstr "कोई मीडिया नहीं बजा रहा है"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "गाना हटाएँ"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "कोई मीडिया नहीं बजा रहा है"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "गीत-सूचीयाँ"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "कलाकार"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "गीत-सूचीयाँ"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "अज्ञात"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "यूट्यूब संगीत पर संगीत ढूंढें"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "अगला बजाएँ"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
#~ msgid "Add to Playlist"
#~ msgstr "गीत-सूची में जोड़ें"
#~ msgid "Play next"
#~ msgstr "अगला बजाएँ"
#~ msgid "Clear"
#~ msgstr "साफ करें"
#~ msgid "Pause"
#~ msgstr "रोकें"
#~ msgid "Expand"
#~ msgstr "विस्तृत करें"
#~ msgid "Play only Audio"
#~ msgstr "केवल ध्वनी चलाएँ"
-236
View File
@@ -1,236 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Linerly <[email protected]>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-09-20 19:34+0700\n"
"Last-Translator: Linerly <[email protected]>\n"
"Language-Team: Indonesian <[email protected]>\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.08.1\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Linerly"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Putar"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Acak"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Buka di Peramban"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artis"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Pemutaran video belum didukung. Apakah Anda ingin memutar audionya dari "
"\"%1\" saja?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Album"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Single"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Lagu"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Video"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Oke"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Batalkan"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favorit"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Tampilkan Semua"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Lagu Favorit"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Paling banyak diputar"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Lagu yang telah diputar"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Kembali"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Cari"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Sekarang Diputarkan"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Antrean"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Hapus Trek"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Tidak ada media yang diputarkan"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Daftar lagu yang tidak diketahui"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Belum ada lagu di sini"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Daftar Putar"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artis"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Daftar Putar"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Tidak diketahui"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Cari musik di YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Putar Berikutnya"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favorit"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favorit"
#~ msgid "Add to Playlist"
#~ msgstr "Tambahkan ke Daftar Putar"
#~ msgid "Play next"
#~ msgstr "Putar berikutnya"
-248
View File
@@ -1,248 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Vincenzo Reale <[email protected]>, 2021, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-09 18:16+0100\n"
"Last-Translator: Vincenzo Reale <[email protected]>\n"
"Language-Team: Italian <[email protected]>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 22.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Vincenzo Reale"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Riproduci"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Mescola"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Apri nel browser"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artisti"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"La riproduzione dei video non è ancora supportata. Vuoi riprodurre solo "
"l'audio di «%1»?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Album"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singoli"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Brani"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Video"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Annulla"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Preferiti"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostra tutto"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Brani preferiti"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Più riprodotti"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Brani riprodotti"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Indietro"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Cerca"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "In riproduzione"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Coda"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Testi"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Rimuovi traccia"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Nessun supporto in riproduzione"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Elenco di brani sconosciuto"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Non c'è ancora alcun brano"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Scalette"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artisti"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Scalette"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Sconosciuto"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Trova musica su Youtube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Riproduci successivo"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Preferiti"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Preferiti"
#~ msgid "Add to Playlist"
#~ msgstr "Aggiungi alla scaletta"
#~ msgid "Play next"
#~ msgstr "Riproduci successivo"
#~ msgid "Clear"
#~ msgstr "Pulisci"
#~ msgid "Pause"
#~ msgstr "Pausa"
#~ msgid "Expand"
#~ msgstr "Espandi"
#~ msgid "Play only Audio"
#~ msgstr "Riproduci solo l'audio"
-221
View File
@@ -1,221 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2021-05-29 19:15-0700\n"
"Last-Translator: Japanese KDE translation team <[email protected]>\n"
"Language-Team: Japanese <[email protected]>\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Accelerator-Marker: &\n"
"X-Text-Markup: kde4\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr ""
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr ""
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr ""
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr ""
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr ""
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr ""
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr ""
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr ""
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr ""
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr ""
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr ""
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr ""
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr ""
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr ""
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr ""
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr ""
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr ""
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr ""
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr ""
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr ""
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
-232
View File
@@ -1,232 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-29 06:58+0100\n"
"Last-Translator: Temuri Doghonadze <[email protected]>\n"
"Language-Team: Georgian <[email protected]>\n"
"Language: ka\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.2\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Temuri Doghonadze"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "დაკვრა"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "შემთხვევით"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "ბრაუზერში გახსნა"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "ალბომი • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "მეტი"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "რადიო"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "შემსრულებელი"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"ვიდეოს დაკვრა ჯერ მხარდაჭერილი არაა. გნებავთ \"%1\"-ის მხოლოდ ხმა დავუკრა?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "ალბომები"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "სინგლები"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "სიმღერები"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "ვიდეო"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "კარგი"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "გაუქმენა"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "რჩეულები"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "ყველას ჩვენება"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "რჩეული სიმღერები"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "ხშირად დაკრულები"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "დაკრული სიმღერები"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "უკან"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "ძებნა"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "მიმდინარეობს დაკვრა"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "რიგი"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "ლირიკა"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "აუდიობილიკის წაშლა"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "არაფერს ვუკრავ"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "უცნობი სიმღერების სია"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "აქ ჯერ სიმღერები არაა"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "დასაკრავი სია"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "შემსრულებლები"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "დასაკრავი სიები"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "უცნობი"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "იპოვეთ მუსიკა YouTube Music-ზე"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "შემდეგის დაკვრა"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "რიგში ჩამატება"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "რჩეულის წაშლა"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "რჩეულის დამატება"
#~ msgid "Add to Playlist"
#~ msgstr "დასაკრავი სიაში დამატება"
#~ msgid "Play next"
#~ msgstr "შემდეგის დაკვრა"
-246
View File
@@ -1,246 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Shinjo Park <[email protected]>, 2021, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-10-03 14:20+0200\n"
"Last-Translator: Shinjo Park <[email protected]>\n"
"Language-Team: Korean <[email protected]>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Lokalize 21.12.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "박신조"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "재생"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "무순서 재생"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "브라우저로 열기"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "라디오"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "아티스트"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "비디오 재생은 지원하지 않습니다. \"%1\"의 오디오만 재생하시겠습니까?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "앨범"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "싱글"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "곡"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "비디오"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "확인"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "취소"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "책갈피"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "모두 표시"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "책갈피에 등록된 노래"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "가장 많이 재생됨"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "재생한 노래"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "뒤로"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "검색"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "지금 재생 중"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "대기열에 추가"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "트랙 삭제"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "재생 중인 미디어 없음"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "알 수 없는 노래 목록"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "목록에 등록된 곡 없음"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "재생 목록"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "아티스트"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "재생 목록"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "알 수 없음"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "YouTube Music에서 음악 찾기"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "다음 재생"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "책갈피"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "책갈피"
#~ msgid "Add to Playlist"
#~ msgstr "재생 목록에 추가"
#~ msgid "Play next"
#~ msgstr "다음 재생"
#~ msgid "Clear"
#~ msgstr "지우기"
#~ msgid "Pause"
#~ msgstr "일시 정지"
#~ msgid "Expand"
#~ msgstr "확장"
#~ msgid "Play only Audio"
#~ msgstr "오디오만 재생"
-244
View File
@@ -1,244 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Freek de Kruijf <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 11:10+0100\n"
"Last-Translator: Freek de Kruijf <[email protected]>\n"
"Language-Team: \n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 22.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Freek de Kruijf - 2021 t/m 2022"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Afspelen"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Willekeurig afspelen"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "In browser openen"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Album • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Meer"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artiest"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Afspelen van video wordt nog niet ondersteund. Wilt u alleen het geluid van "
"\"%1\" afspelen?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albums"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Enkelvoudige"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Songs"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Video's"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Annuleren"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favorieten"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Alles tonen"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Favoriete songs"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Meest afgespeeld"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Afgespeelde songs"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Terug"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Zoeken"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Speelt nu"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Wachtrij"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Liedteksten"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Track verwijderen"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Er speelt geen medium"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Onbekende lijst met songs"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Er zijn hier nog geen songs"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Afspeellijst"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artiesten"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Afspeellijsten"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Onbekend"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Muziek zoeken op YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Volgende afspelen"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Aan wachtrij toevoegen"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Favoriet verwijderen"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Favoriet toevoegen"
#~ msgid "Add to Playlist"
#~ msgstr "Aan afspeellijst toevoegen"
#~ msgid "Play next"
#~ msgstr "Volgende afspelen"
#~ msgid "Clear"
#~ msgstr "Wissen"
#~ msgid "Pause"
#~ msgstr "Pauzeren"
#~ msgid "Expand"
#~ msgstr "Uitbreiden"
#~ msgid "Play only Audio"
#~ msgstr "Alleen geluid afspelen"
-202
View File
@@ -1,202 +0,0 @@
# Translation of audiotube to Norwegian Nynorsk
#
# Øystein Steffensen-Alværvik <[email protected]>, 2022.
# Karl Ove Hufthammer <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-23 00:50+0000\n"
"PO-Revision-Date: 2022-09-11 19:45+0200\n"
"Last-Translator: Karl Ove Hufthammer <[email protected]>\n"
"Language-Team: Norwegian Nynorsk <[email protected]>\n"
"Language: nn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 22.08.1\n"
"X-Environment: kde\n"
"X-Accelerator-Marker: &\n"
"X-Text-Markup: kde4\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Øystein Steffensen-Alværvik"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:19 contents/ui/PlaylistPage.qml:19
#, kde-format
msgid "Play"
msgstr "Spel av"
#: contents/ui/AlbumPage.qml:26 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Shuffle"
msgstr "Stokk om"
#: contents/ui/AlbumPage.qml:35 contents/ui/ArtistPage.qml:19
#, kde-format
msgid "Open in Browser"
msgstr "Opna i nettlesar"
#: contents/ui/AlbumPage.qml:78 contents/ui/ArtistPage.qml:100
#: contents/ui/PlaybackHistory.qml:74 contents/ui/PlaylistPage.qml:68
#, kde-format
msgid "Play Next"
msgstr "Spel som neste"
#: contents/ui/AlbumPage.qml:83 contents/ui/ArtistPage.qml:106
#: contents/ui/PlaybackHistory.qml:79 contents/ui/PlaylistPage.qml:73
#: contents/ui/SearchPage.qml:122
#, kde-format
msgid "Add to Playlist"
msgstr "Legg til speleliste"
#: contents/ui/ArtistPage.qml:47 contents/ui/SearchPage.qml:67
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "Videoavspeling er endå ikkje støtta. Vil du spela av lyden til «%1»?"
#: contents/ui/ArtistPage.qml:63 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Album"
#: contents/ui/ArtistPage.qml:65
#, kde-format
msgid "Singles"
msgstr "Singelplater"
#: contents/ui/ArtistPage.qml:67 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Songar"
#: contents/ui/ArtistPage.qml:69 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videoar"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Avbryt"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favorittar"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:187
#, kde-format
msgid "Show All"
msgstr "Vis alle"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Favorittsongar"
#: contents/ui/LibraryPage.qml:177
#, kde-format
msgid "Most played"
msgstr "Oftast avspela"
#: contents/ui/LibraryPage.qml:192
#, kde-format
msgid "Played Songs"
msgstr "Avspela songar"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Tilbake"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Søk"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Spelar no"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Kø"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:292
#, kde-format
msgid "Remove Track"
msgstr "Fjern spor"
#: contents/ui/MaximizedPlayerPage.qml:334
#: contents/ui/MinimizedPlayerControls.qml:96
#, kde-format
msgid "No media playing"
msgstr "Inkje medium vert spela"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Ukjend songliste"
#: contents/ui/PlaybackHistory.qml:25
#, kde-format
msgid "No songs here yet"
msgstr "Ingen songar enno"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistar"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Spelelister"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Ukjend"
#: contents/ui/SearchPage.qml:116
#, kde-format
msgid "Play next"
msgstr "Spel som neste"
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Søk etter musikk på YouTube Music"
-245
View File
@@ -1,245 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# A S Alam <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-01-01 09:11-0800\n"
"Last-Translator: A S Alam <[email protected]>\n"
"Language-Team: Punjabi <[email protected]>\n"
"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 21.04.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "ਅਮਨਪਰੀਤ ਸਿੰਘ ਆਲਮ ੨੦੨੧"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "alam yellow @ gmail.com"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "ਚਲਾਓ"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "ਰਲਵੇਂ"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "ਰੇਡੀਓ"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "ਕਲਾਕਾਰ"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "ਵਿਡੀਓ ਪਲੇਅਬੈਕ ਹਾਲੇ ਸਹਾਇਕ ਨਹੀਂ ਹੈ। ਕੀ ਤੁਸੀਂ \"%1\" ਦਾ ਆਡੀਓ ਹੀ ਚਲਾਉਣਾ ਚਾਹੋਗੇ?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "ਐਲਬਮਾਂ"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "ਇਕੱਲੇ"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "ਗੀਤ"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "ਵਿਡੀਓ"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "ਠੀਕ ਹੈ"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "ਰੱਦ ਕਰੋ"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "ਖੋਜੋ"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "ਆਡੀਓ-ਟਿਊਬ"
#: contents/ui/MaximizedPlayerPage.qml:105
#, fuzzy, kde-format
#| msgid "No media playing"
msgid "Now Playing"
msgstr "ਕੋਈ ਮੀਡੀਆ ਨਹੀਂ ਚੱਲਦਾ"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "ਟਰੈਕ ਨੂੰ ਹਟਾਓ"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "ਕੋਈ ਮੀਡੀਆ ਨਹੀਂ ਚੱਲਦਾ"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "ਪਲੇਅਲਿਸਟਾਂ"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "ਕਲਾਕਾਰ"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "ਪਲੇਅਲਿਸਟਾਂ"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "ਅਣਪਛਾਤਾ"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "ਯੂਟਿਊਬ ਸੰਗੀਤ ਉੱਤੇ ਸੰਗੀਤ ਲੱਭੋ"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "ਅਗਲਾ ਚਲਾਓ"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
#~ msgid "Add to Playlist"
#~ msgstr "ਪਲੇਅਲਿਸਟ ਵਿੱਚ ਜੋੜੋ"
#~ msgid "Play next"
#~ msgstr "ਅਗਲਾ ਚਲਾਓ"
#~ msgid "Clear"
#~ msgstr "ਮਿਟਾਓ"
#~ msgid "Pause"
#~ msgstr "ਵਿਰਾਮ"
#~ msgid "Expand"
#~ msgstr "ਫੈਲਾਓ"
#~ msgid "Play only Audio"
#~ msgstr "ਸਿਰਫ਼ ਆਡੀਓ ਹੀ ਚਲਾਓ"
-250
View File
@@ -1,250 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Łukasz Wojniłowicz <[email protected]>, 2021, 2022.
# Ignacy Kajdan <[email protected]>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-10-18 18:06+0200\n"
"Last-Translator: Łukasz Wojniłowicz <[email protected]>\n"
"Language-Team: Polish <[email protected]>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
"X-Generator: Lokalize 22.08.1\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Łukasz Wojniłowicz, Ignacy Kajdan"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected], [email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Odtwórz"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Wymieszaj"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Otwórz w przeglądarce"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Wykonawcy"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Odtwarzanie wideo nie jest jeszcze obsługiwane. Czy odtworzyć tylko dźwięk z "
"„%1”?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albumy"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Single"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Piosenki"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Filmy"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Anuluj"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Ulubione"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Pokaż wszystkie"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Ulubione utwory"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Najczęściej odtwarzane"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Odtwarzane utwory"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Wstecz"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Szukaj"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Teraz odtwarzany"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Kolejka"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Usuń utwór"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Nic nie jest odtwarzane"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Nieznana lista utworów"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Jeszcze nie ma tutaj utworów"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Listy odtwarzania"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Wykonawcy"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Listy odtwarzania"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Nieznany"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Znajdź muzykę w serwisie YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Odtwórz następny"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Ulubione"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Ulubione"
#~ msgid "Add to Playlist"
#~ msgstr "Dodaj do listy odtwarzania"
#~ msgid "Play next"
#~ msgstr "Odtwórz następny"
#~ msgid "Clear"
#~ msgstr "Wyczyść"
#~ msgid "Pause"
#~ msgstr "Wstrzymaj"
#~ msgid "Expand"
#~ msgstr "Rozwiń"
#~ msgid "Play only Audio"
#~ msgstr "Odtwórz tylko dźwięk"
-228
View File
@@ -1,228 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 18:35+0000\n"
"Last-Translator: José Nuno Coelho Pires <[email protected]>\n"
"Language-Team: Portuguese <[email protected]>\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-POFile-SpellExtra: Singles AudioTube YouTube Music\n"
"X-POFile-IgnoreConsistency: Singles\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "José Nuno Pires"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Reproduzir"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Baralhar"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Abrir no Navegador"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Álbum • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Mais"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Rádio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Artista"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"A reprodução de vídeos ainda não é suportada. Deseja reproduzir apenas o "
"áudio de \"%1\"?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Álbuns"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "'Singles'"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Músicas"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vídeos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancelar"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoritos"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostrar Tudo"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Músicas Favoritas"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Mais Tocadas"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Músicas Tocadas"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Recuar"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Procurar"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Agora a Ouvir"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Fila"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Letras Musicais"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Remover a Faixa"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Nada em reprodução"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Lista de músicas desconhecida"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Ainda sem músicas aqui"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Lista de Reprodução"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistas"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Listas de Reprodução"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Desconhecido"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Procurar música no YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Reproduzir o Seguinte"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Adicionar à fila"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Remover o Favorito"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Adicionar aos Favoritos"
-249
View File
@@ -1,249 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Luiz Fernando Ranghetti <[email protected]>, 2021, 2022.
# Thiago Masato Costa Sueto <[email protected]>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-10-19 15:40-0300\n"
"Last-Translator: Luiz Fernando Ranghetti <[email protected]>\n"
"Language-Team: Portuguese <[email protected]>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Lokalize 21.12.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Luiz Fernando Ranghetti"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Reproduzir"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Embaralhar"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Abrir no navegador"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Rádio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artistas"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"A reprodução de vídeos ainda não é suportada. Deseja reproduzir apenas o "
"áudio de \"%1\"?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Álbuns"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singles"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Músicas"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Vídeos"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Cancelar"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoritos"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Mostrar tudo"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Músicas favoritas"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Mais reproduzidas"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Músicas reproduzidas"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Voltar"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Pesquisar"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Reproduzindo agora"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Fila"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Remover faixa"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Nenhuma mídia em reprodução"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Lista de músicas desconhecida"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Nenhuma música ainda"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Listas de músicas"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artistas"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Listas de músicas"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Desconhecido"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Encontre músicas no YouTube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Reproduzir próxima"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favoritos"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favoritos"
#~ msgid "Add to Playlist"
#~ msgstr "Adicionar à lista de reprodução"
#~ msgid "Play next"
#~ msgstr "Reproduzir próxima"
#~ msgid "Clear"
#~ msgstr "Limpar"
#~ msgid "Pause"
#~ msgstr "Pausar"
#~ msgid "Expand"
#~ msgstr "Expandir"
#~ msgid "Play only Audio"
#~ msgstr "Reproduzir apenas áudio"
-232
View File
@@ -1,232 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Alexander Yavorsky <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-05-10 21:39+0300\n"
"Last-Translator: Alexander Yavorsky <[email protected]>\n"
"Language-Team: Russian <[email protected]>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n"
"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 21.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Александр Яворский"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Воспроизвести"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Перемешать"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Радио"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr ""
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Альбомы"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr ""
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr ""
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Видеозаписи"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "ОК"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Отмена"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Поиск"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Удалить дорожку"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr ""
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Play"
msgid "Playlist"
msgstr "Воспроизвести"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr ""
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr ""
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr ""
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr ""
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Воспроизвести следующий"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
#~ msgid "Clear"
#~ msgstr "Очистить"
#~ msgid "Pause"
#~ msgstr "Приостановить"
-238
View File
@@ -1,238 +0,0 @@
# translation of audiotube.po Slovak
# Roman Paholik <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-04-07 18:01+0200\n"
"Last-Translator: Roman Paholik <[email protected]>\n"
"Language-Team: Slovak <[email protected]>\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 21.12.3\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Roman Paholík"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Prehrať"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Zamiešať"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Rádio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Interpreti"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albumy"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Samotné"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr ""
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videá"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "OK"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Zrušiť"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Hľadať"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, fuzzy, kde-format
#| msgid "No media playing"
msgid "Now Playing"
msgstr "Neprehráva sa žiadne médium"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Neprehráva sa žiadne médium"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Zoznamy skladieb"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Interpreti"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Zoznamy skladieb"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Neznáme"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr ""
#: contents/ui/SongMenu.qml:26
#, fuzzy, kde-format
#| msgid "Play"
msgid "Play Next"
msgstr "Prehrať"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
#~ msgid "Add to Playlist"
#~ msgstr "Pridať do zoznamu skladieb"
#~ msgid "Clear"
#~ msgstr "Vyčistiť"
#~ msgid "Pause"
#~ msgstr "Pozastaviť"
#~ msgid "Expand"
#~ msgstr "Rozvinúť"
-243
View File
@@ -1,243 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Matjaž Jeran <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-29 07:45+0100\n"
"Last-Translator: Matjaž Jeran <[email protected]>\n"
"Language-Team: Slovenian <[email protected]>\n"
"Language: sl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 22.08.3\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n"
"%100==4 ? 3 : 0);\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Matjaž Jeran"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Predvajaj"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Premešaj"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Odpri v brskalniku"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Album • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Več"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Avtor"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "Video playback še ni podprt. Ali želite predvajati samo zvok \"%1\"?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albumi"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singli"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Glasba"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videposnetki"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "V redu"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Prekliči"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Priljubljene"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Prikaži vse"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Priljubljene pesmi"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Največkrat predvajane"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Predvajane pesmi"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Nazaj"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Išči"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Pravkar predvajano"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Čakalna vrsta"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Besedilo"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Odstrani sled"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Niče se ne predvaja"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Neznan seznam pesmi"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Tu še ni nobene pesmi"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Seznam predvajanja"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Avtorji"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Seznami predvajanja"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Neznano"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Najdi glasbo na Youtube Music"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Predvajaj naslednjo"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Dodaj v v čakalno vrsto"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Odstrani priljubljenega"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Dodaj priljubljenega"
#~ msgid "Add to Playlist"
#~ msgstr "Dodaj v seznam predvajanja"
#~ msgid "Play next"
#~ msgstr "Predvajaj naslednjo"
#~ msgid "Clear"
#~ msgstr "Očisti"
#~ msgid "Pause"
#~ msgstr "Premor"
#~ msgid "Expand"
#~ msgstr "Razširi"
#~ msgid "Play only Audio"
#~ msgstr "Predvajaj samo zvok"
-246
View File
@@ -1,246 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Stefan Asserhäll <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-09-24 14:57+0200\n"
"Last-Translator: Stefan Asserhäll <[email protected]>\n"
"Language-Team: Swedish <[email protected]>\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 20.08.1\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Stefan Asserhäll"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Spela"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Blanda"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Öppna i webbläsare"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radio"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "Artister"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "Videouppspelning stöds inte ännu. Vill du bara spela ljudet på \"%1\"?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Album"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Singlar"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Låtar"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videor"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Ok"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Avbryt"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Favoriter"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Visa alla"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Favoritlåtar"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Mest spelade"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Spelade låtar"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Bakåt"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Sök"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Spelar nu"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Köa"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Ta bort spår"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Inga media spelar"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Okänd låtlista"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Inga låtar här ännu"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "Spellistor"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Artister"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Spellistor"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Okänd"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Sök efter musik på YouTube musik"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Spela nästa"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "Favoriter"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "Favoriter"
#~ msgid "Add to Playlist"
#~ msgstr "Lägg till i spellista"
#~ msgid "Play next"
#~ msgstr "Spela nästa"
#~ msgid "Clear"
#~ msgstr "Rensa"
#~ msgid "Pause"
#~ msgstr "Paus"
#~ msgid "Expand"
#~ msgstr "Expandera"
#~ msgid "Play only Audio"
#~ msgstr "Spela bara ljud"
-241
View File
@@ -1,241 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the audiotube package.
#
# Emir SARI <[email protected]>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 18:46+0300\n"
"Last-Translator: Emir SARI <[email protected]>\n"
"Language-Team: Turkish <[email protected]>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Lokalize 22.08.3\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Emir SARI"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Çal"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Karıştır"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Tarayıcıda Aç"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Albüm • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Daha Fazla"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Radyo"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Sanatçı"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Video oynatımı henüz desteklenmiyor. \"%1\" videosunun yalnızca sesinin "
"çalınmasını ister misiniz?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Albümler"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Tekliler"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Şarkılar"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Videolar"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Tamam"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "İptal"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Beğeniler"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Tümünü Göster"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Beğenilen Şarkılar"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "En Çok Çalınanlar"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Çalınan Şarkılar"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Geri"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Ara"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Şimdi Çalınıyor"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Kuyruk"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Şarkı Sözleri"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Parçayı Kaldır"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Ortam çalmıyor"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Bilinmeyen şarkılar listesi"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Burada henüz şarkı yok"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Çalma Listesi"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Sanatçılar"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Çalma Listeleri"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Bilinmiyor"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "YouTube Müzik'te müzik bul"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Sonrakini Çal"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Kuyruğa ekle"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Beğenilerden Kaldır"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Beğenilere Ekle"
#~ msgid "Add to Playlist"
#~ msgstr "Çalma Listesine Ekle"
#~ msgid "Play next"
#~ msgstr "Sonrakini çal"
#~ msgid "Clear"
#~ msgstr "Temizle"
#~ msgid "Pause"
#~ msgstr "Duraklat"
#~ msgid "Expand"
#~ msgstr "Genişlet"
-245
View File
@@ -1,245 +0,0 @@
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the PACKAGE package.
#
# Yuri Chornoivan <[email protected]>, 2021, 2022.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-28 20:43+0200\n"
"Last-Translator: Fracture dept <[email protected]>\n"
"Language-Team: Ukrainian <[email protected]>\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n"
"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 20.12.0\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Юрій Чорноіван"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "Відтворити"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "Перемішати"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "Відкрити у переглядачі"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr "Альбом • %1"
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr "Більше"
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "Радіо"
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr "Виконавець"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
"Підтримки відтворення відео ще не передбачено. Хочете, щоб було відтворено "
"лише звукову частину «%1»?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "Альбоми"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "Одинарні"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "Пісні"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "Відео"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "Гаразд"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "Скасувати"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "Улюблені"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "Показати всі"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "Улюблені композиції"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "Часто слухали"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "Відтворені композиції"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "Назад"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "Пошук"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "Зараз відтворюємо"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "Додати до черги"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr "Текст"
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "Вилучити композицію"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "Відтворення не відбувається"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "Невідомий список композицій"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "Тут ще немає композицій"
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr "Список відтворення"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "Виконавці"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "Списки відтворення"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "Невідомий"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "Шукати музику на сайті «Музика Youtube»"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "Відтворити наступну"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr "Додати до черги"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr "Вилучити з улюблених"
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr "Додати до улюблених"
#~ msgid "Add to Playlist"
#~ msgstr "Додати до списку відтворення"
#~ msgid "Play next"
#~ msgstr "Відтворити наступну"
#~ msgid "Clear"
#~ msgstr "Спорожнити"
#~ msgid "Pause"
#~ msgstr "Призупинити"
#~ msgid "Expand"
#~ msgstr "Розгорнути"
#~ msgid "Play only Audio"
#~ msgstr "Відтворити лише звук"
-234
View File
@@ -1,234 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: kdeorg\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-11-19 14:49\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: kdeorg\n"
"X-Crowdin-Project-ID: 269464\n"
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /kf5-trunk/messages/audiotube/audiotube.pot\n"
"X-Crowdin-File-ID: 25474\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr "Qi Binye, Tyson Tan"
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr "[email protected], [email protected]"
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr "播放"
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr "随机播放"
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr "在浏览器中打开"
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr "电台"
#: contents/ui/ArtistPage.qml:46
#, fuzzy, kde-format
#| msgid "Artists"
msgid "Artist"
msgstr "艺人"
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr "视频播放尚未被支持。您想要只播放“%1”中的音频吗?"
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr "专辑"
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr "单曲"
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr "曲目"
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr "视频"
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr "确定"
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr "取消"
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr "收藏夹"
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr "显示全部"
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr "收藏的歌曲"
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr "最常播放"
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr "已播放歌曲"
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr "返回"
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr "搜索"
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr "AudioTube"
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr "正在播放"
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr "播放队列"
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr "移除曲目"
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr "没有正在播放的媒体"
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr "未知歌曲列表"
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr "此处无歌曲"
#: contents/ui/PlaylistPage.qml:42
#, fuzzy, kde-format
#| msgid "Playlists"
msgid "Playlist"
msgstr "播放列表"
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr "艺人"
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr "播放列表"
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr "未知"
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr "查找 YouTube Music 中的音乐"
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr "播放下一首"
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Remove Favourite"
msgstr "收藏夹"
#: contents/ui/SongMenu.qml:39
#, fuzzy, kde-format
#| msgid "Favourites"
msgid "Add Favourite"
msgstr "收藏夹"
#~ msgid "Add to Playlist"
#~ msgstr "添加到播放列表"
#~ msgid "Play next"
#~ msgstr "播放下一首"
-225
View File
@@ -1,225 +0,0 @@
# Chinese translations for audiotube package
# audiotube 套件的正體中文翻譯.
# Copyright (C) 2022 This file is copyright:
# This file is distributed under the same license as the audiotube package.
# Automatically generated, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: audiotube\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2022-11-28 00:51+0000\n"
"PO-Revision-Date: 2022-05-30 00:47+0000\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#, kde-format
msgctxt "NAME OF TRANSLATORS"
msgid "Your names"
msgstr ""
#, kde-format
msgctxt "EMAIL OF TRANSLATORS"
msgid "Your emails"
msgstr ""
#: contents/ui/AlbumPage.qml:22 contents/ui/PlaylistPage.qml:26
#, kde-format
msgid "Play"
msgstr ""
#: contents/ui/AlbumPage.qml:29 contents/ui/ArtistPage.qml:32
#: contents/ui/PlaylistPage.qml:33
#, kde-format
msgid "Shuffle"
msgstr ""
#: contents/ui/AlbumPage.qml:37 contents/ui/ArtistPage.qml:39
#, kde-format
msgid "Open in Browser"
msgstr ""
#: contents/ui/AlbumPage.qml:44
#, kde-format
msgid "Album • %1"
msgstr ""
#: contents/ui/AlbumPage.qml:87 contents/ui/ArtistPage.qml:125
#: contents/ui/PlaybackHistory.qml:72 contents/ui/PlaylistPage.qml:89
#: contents/ui/SearchPage.qml:128
#, kde-format
msgid "More"
msgstr ""
#: contents/ui/ArtistPage.qml:27
#, kde-format
msgid "Radio"
msgstr ""
#: contents/ui/ArtistPage.qml:46
#, kde-format
msgid "Artist"
msgstr ""
#: contents/ui/ArtistPage.qml:68 contents/ui/SearchPage.qml:69
#, kde-format
msgid ""
"Video playback is not supported yet. Do you want to play only the audio of "
"\"%1\"?"
msgstr ""
#: contents/ui/ArtistPage.qml:85 contents/ui/SearchPage.qml:25
#, kde-format
msgid "Albums"
msgstr ""
#: contents/ui/ArtistPage.qml:87
#, kde-format
msgid "Singles"
msgstr ""
#: contents/ui/ArtistPage.qml:89 contents/ui/SearchPage.qml:31
#, kde-format
msgid "Songs"
msgstr ""
#: contents/ui/ArtistPage.qml:91 contents/ui/SearchPage.qml:33
#, kde-format
msgid "Videos"
msgstr ""
#: contents/ui/ConfirmationMessage.qml:16
#, kde-format
msgid "OK"
msgstr ""
#: contents/ui/ConfirmationMessage.qml:21
#, kde-format
msgid "Cancel"
msgstr ""
#: contents/ui/LibraryPage.qml:21
#, kde-format
msgid "Favourites"
msgstr ""
#: contents/ui/LibraryPage.qml:31 contents/ui/LibraryPage.qml:181
#, kde-format
msgid "Show All"
msgstr ""
#: contents/ui/LibraryPage.qml:36
#, kde-format
msgid "Favourite Songs"
msgstr ""
#: contents/ui/LibraryPage.qml:171
#, kde-format
msgid "Most played"
msgstr ""
#: contents/ui/LibraryPage.qml:186
#, kde-format
msgid "Played Songs"
msgstr ""
#: contents/ui/main.qml:49
#, kde-format
msgid "Back"
msgstr ""
#: contents/ui/main.qml:83 contents/ui/SearchPage.qml:149
#, kde-format
msgid "Search"
msgstr ""
#: contents/ui/main.qml:95
#, kde-format
msgid "AudioTube"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:105
#, kde-format
msgid "Now Playing"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:130
#, kde-format
msgid "Queue"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:155
#, kde-format
msgid "Lyrics"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:270
#, kde-format
msgid "Remove Track"
msgstr ""
#: contents/ui/MaximizedPlayerPage.qml:312
#: contents/ui/MinimizedPlayerControls.qml:91
#, kde-format
msgid "No media playing"
msgstr ""
#: contents/ui/PlaybackHistory.qml:15
#, kde-format
msgid "Unknown list of songs"
msgstr ""
#: contents/ui/PlaybackHistory.qml:27
#, kde-format
msgid "No songs here yet"
msgstr ""
#: contents/ui/PlaylistPage.qml:42
#, kde-format
msgid "Playlist"
msgstr ""
#: contents/ui/SearchPage.qml:27
#, kde-format
msgid "Artists"
msgstr ""
#: contents/ui/SearchPage.qml:29
#, kde-format
msgid "Playlists"
msgstr ""
#: contents/ui/SearchPage.qml:36
#, kde-format
msgid "Unknown"
msgstr ""
#: contents/ui/SearchPage.qml:146
#, kde-format
msgid "Find music on YouTube Music"
msgstr ""
#: contents/ui/SongMenu.qml:26
#, kde-format
msgid "Play Next"
msgstr ""
#: contents/ui/SongMenu.qml:32
#, kde-format
msgid "Add to queue"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Remove Favourite"
msgstr ""
#: contents/ui/SongMenu.qml:39
#, kde-format
msgid "Add Favourite"
msgstr ""
-7
View File
@@ -1,7 +0,0 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
# SPDX-FileCopyrightText: 2021 Kuba Prussak <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
ytmusicapi==0.24.0
yt-dlp
+7 -31
View File
@@ -1,22 +1,9 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: BSD-2-Clause
add_library(ytm STATIC
ytmusic.cpp
)
target_link_libraries(ytm PUBLIC pybind11::embed)
target_include_directories(ytm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(ytm PRIVATE -DRANDALL_WAS_HERE)
add_library(asyncdatabase STATIC
asyncdatabase.cpp
asyncdatabase_p.h
)
target_include_directories(asyncdatabase PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(asyncdatabase PUBLIC Qt::Sql)
add_subdirectory(example)
@@ -27,32 +14,21 @@ add_executable(audiotube
albummodel.cpp
videoinfoextractor.cpp
artistmodel.cpp
userplaylistmodel.cpp
playlistmodel.cpp
playlistutils.cpp
errorhandler.cpp
playerutils.cpp
abstractytmusicmodel.cpp
multiiterableview.h
library.cpp
resources.qrc
)
target_link_libraries(audiotube
Qt::Core
Qt::Gui
Qt::Qml
Qt::Quick
Qt::QuickControls2
Qt::Svg
Qt::Widgets
Qt5::Core
Qt5::Gui
Qt5::Qml
Qt5::Quick
Qt5::QuickControls2
Qt5::Svg
KF5::I18n
KF5::CoreAddons
KF5::Crash
ytm
asyncdatabase
)
target_compile_definitions(audiotube PRIVATE -DQT_NO_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING)
target_compile_definitions(audiotube PRIVATE -DQT_NO_KEYWORDS)
install(TARGETS audiotube ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
-4
View File
@@ -1,4 +0,0 @@
#! /usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Tobias Fella <[email protected]>
# SPDX-License-Identifier: CC0-1.0
$XGETTEXT `find -name \*.cpp -o -name \*.qml -o -name \*.js` -o $podir/audiotube.pot
-27
View File
@@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "abstractytmusicmodel.h"
#include "asyncytmusic.h"
AbstractYTMusicModel::AbstractYTMusicModel(QObject *parent)
: QAbstractListModel(parent)
{
}
bool AbstractYTMusicModel::loading() const
{
return m_loading;
}
void AbstractYTMusicModel::setLoading(bool loading)
{
if (m_loading == loading)
return;
m_loading = loading;
Q_EMIT loadingChanged();
}
-28
View File
@@ -1,28 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QAbstractListModel>
///
/// Base class for all models that work with data from the AsyncYTMusic class.
/// It provides a loading property, that is automatically set to false if a query fails.
///
class AbstractYTMusicModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool loading READ loading WRITE setLoading NOTIFY loadingChanged)
public:
explicit AbstractYTMusicModel(QObject *parent = nullptr);
bool loading() const;
void setLoading(bool loading);
Q_SIGNAL void loadingChanged();
private:
bool m_loading = false;
};
+28 -46
View File
@@ -1,60 +1,44 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include "albummodel.h"
#include "playlistutils.h"
#include <QStringBuilder>
#include <QDebug>
AlbumModel::AlbumModel(QObject *parent)
: AbstractYTMusicModel(parent)
: QAbstractListModel(parent)
{
connect(this, &AlbumModel::browseIdChanged, this, [this] {
connect(this, &AlbumModel::browseIdChanged, this, [=] {
setLoading(true);
auto future = YTMusicThread::instance()->fetchAlbum(m_browseId);
connectFuture(future, this, [=, this](const album::Album &album) {
setLoading(false);
beginResetModel();
m_album = album;
endResetModel();
std::sort(m_album.thumbnails.begin(), m_album.thumbnails.end());
Q_EMIT titleChanged();
Q_EMIT artistsChanged();
Q_EMIT thumbnailUrlChanged();
Q_EMIT playlistIdChanged();
});
AsyncYTMusic::instance().fetchAlbum(m_browseId);
});
connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, [=, this] {
connect(&AsyncYTMusic::instance(), &AsyncYTMusic::fetchAlbumFinished, this, [=](const album::Album &album) {
setLoading(false);
beginResetModel();
m_album = album;
endResetModel();
std::sort(m_album.thumbnails.begin(), m_album.thumbnails.end());
Q_EMIT titleChanged();
Q_EMIT thumbnailUrlChanged();
Q_EMIT playlistIdChanged();
});
}
int AlbumModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : int(m_album.tracks.size());
return parent.isValid() ? 0 : m_album.tracks.size();
}
QVariant AlbumModel::data(const QModelIndex &index, int role) const
{
switch (role) {
case Title:
return QString::fromStdString(m_album.tracks[index.row()].title);
return QString::fromStdString(m_album.tracks.at(index.row()).title);
case VideoId:
return QString::fromStdString(m_album.tracks[index.row()].video_id.value_or(std::string()));
case Artists:
return QVariant::fromValue(m_album.tracks[index.row()].artists);
case ThumbnailUrl:
if (!m_album.thumbnails.empty()) {
return QString::fromStdString(m_album.thumbnails.front().url);
} else {
return {};
}
case ArtistsDisplayString:
return PlaylistUtils::artistsToString(m_album.tracks[index.row()].artists);
return QString::fromStdString(m_album.tracks.at(index.row()).video_id.value_or(std::string()));
}
Q_UNREACHABLE();
@@ -66,10 +50,7 @@ QHash<int, QByteArray> AlbumModel::roleNames() const
{
return {
{Title, "title"},
{VideoId, "videoId"},
{Artists, "artists"},
{ThumbnailUrl, "thumbnailUrl"},
{ArtistsDisplayString, "artistsDisplayString"}
{VideoId, "videoId"}
};
}
@@ -89,9 +70,15 @@ QString AlbumModel::title() const
return QString::fromStdString(m_album.title);
}
QString AlbumModel::artists() const
bool AlbumModel::loading() const
{
return PlaylistUtils::artistsToString(m_album.artists);
return m_loading;
}
void AlbumModel::setLoading(bool loading)
{
m_loading = loading;
Q_EMIT loadingChanged();
}
@@ -106,10 +93,5 @@ QUrl AlbumModel::thumbnailUrl() const
QString AlbumModel::playlistId() const
{
return QString::fromStdString(m_album.audio_playlist_id);
}
QUrl AlbumModel::webUrl() const
{
return QUrl(YTMUSIC_WEB_BASE_URL % "/playlist?list=" % QString::fromStdString(m_album.audio_playlist_id));
return QString::fromStdString(m_album.playlist_id);
}
+9 -14
View File
@@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,26 +8,21 @@
#include <QUrl>
#include "asyncytmusic.h"
#include "abstractytmusicmodel.h"
class AlbumModel : public AbstractYTMusicModel
class AlbumModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString browseId READ browseId WRITE setBrowseId NOTIFY browseIdChanged REQUIRED)
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(QString artists READ artists NOTIFY artistsChanged)
Q_PROPERTY(QUrl thumbnailUrl READ thumbnailUrl NOTIFY thumbnailUrlChanged)
Q_PROPERTY(bool loading READ loading WRITE setLoading NOTIFY loadingChanged)
Q_PROPERTY(QString playlistId READ playlistId NOTIFY playlistIdChanged)
Q_PROPERTY(QUrl webUrl READ webUrl NOTIFY webUrlChanged)
public:
enum Role {
Title = Qt::UserRole + 1,
VideoId,
Artists,
ThumbnailUrl,
ArtistsDisplayString
VideoId
};
explicit AlbumModel(QObject *parent = nullptr);
@@ -43,19 +38,19 @@ public:
QString title() const;
Q_SIGNAL void titleChanged();
QString artists() const;
Q_SIGNAL void artistsChanged();
bool loading() const;
void setLoading(bool loading);
Q_SIGNAL void loadingChanged();
QUrl thumbnailUrl() const;
Q_SIGNAL void thumbnailUrlChanged();
QString playlistId() const;
Q_SIGNAL void playlistIdChanged();
QUrl webUrl() const;
Q_SIGNAL void webUrlChanged();
private:
QString m_browseId;
bool m_loading;
album::Album m_album;
};
+107 -111
View File
@@ -1,129 +1,110 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include "artistmodel.h"
#include <QStringBuilder>
#include <asyncytmusic.h>
#include <QDebug>
ArtistModel::ArtistModel(QObject *parent)
: AbstractYTMusicModel(parent)
, m_view(albums, singles, songs, videos)
: QAbstractListModel(parent)
{
connect(this, &ArtistModel::channelIdChanged, this, [this] {
connect(this, &ArtistModel::channelIdChanged, this, [=] {
if (m_channelId.isEmpty()) {
return;
}
setLoading(true);
auto future = YTMusicThread::instance()->fetchArtist(m_channelId);
connectFuture(future, this, [=, this](const artist::Artist &artist) {
setLoading(false);
beginResetModel();
m_artist = artist;
std::sort(m_artist.thumbnails.begin(), m_artist.thumbnails.end());
albums = m_artist.albums ? m_artist.albums->results : std::vector<artist::Artist::Album>();
for (auto &album : albums) {
std::sort(album.thumbnails.begin(), album.thumbnails.end());
}
singles = m_artist.singles ? m_artist.singles->results : std::vector<artist::Artist::Single>();
for (auto &single : singles) {
std::sort(single.thumbnails.begin(), single.thumbnails.end());
}
songs = m_artist.songs ? m_artist.songs->results : std::vector<artist::Artist::Song>();
for (auto &song : songs) {
std::sort(song.thumbnails.begin(), song.thumbnails.end());
}
videos = m_artist.videos ? m_artist.videos->results : std::vector<artist::Artist::Video>();
for (auto &video : videos) {
std::sort(video.thumbnails.begin(), video.thumbnails.end());
}
// std::span can't know if the data pointer underneath it was changed, so re-create
m_view = MultiIterableView(albums, singles, songs, videos);
endResetModel();
Q_EMIT titleChanged();
Q_EMIT thumbnailUrlChanged();
});
AsyncYTMusic::instance().fetchArtist(m_channelId);
});
connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, [this] {
connect(&AsyncYTMusic::instance(), &AsyncYTMusic::fetchArtistFinished, this, [=](const artist::Artist &artist) {
setLoading(false);
beginResetModel();
m_artist = artist;
std::sort(m_artist.thumbnails.begin(), m_artist.thumbnails.end());
endResetModel();
Q_EMIT titleChanged();
Q_EMIT thumbnailUrlChanged();
});
}
template <typename T>
int countItems(const std::optional<artist::Artist::Section<T>> &section) {
if (!section.has_value()) {
return 0;
}
return section.value().results.size();
};
int ArtistModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_view.size();
return parent.isValid() ? 0 : countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles)
+ countItems(m_artist.videos);
}
QVariant ArtistModel::data(const QModelIndex &index, int role) const
{
switch (role) {
case Title:
return QString::fromStdString(std::visit([&](auto&& item) {
using T = std::decay_t<decltype(item)>;
if constexpr(std::is_same_v<T, artist::Artist::Album>) {
return item.title;
} else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
return item.title;
} else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
return item.title;
} else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
return item.title;
}
if (index.row() >= countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles)) {
Q_UNREACHABLE();
}, m_view[index.row()]));
int videoIndex = index.row() - (countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles));
if (m_artist.videos.has_value()) {
return QString::fromStdString(m_artist.videos.value().results[videoIndex].title);
}
break;
} else if (index.row() >= countItems(m_artist.songs) + countItems(m_artist.albums)) {
int singleIndex = index.row() - (countItems(m_artist.songs) + countItems(m_artist.albums));
if (m_artist.singles.has_value()) {
return QString::fromStdString(m_artist.singles.value().results[singleIndex].title);
}
break;
} else if (index.row() >= countItems(m_artist.songs)) {
int albumIndex = index.row() - countItems(m_artist.songs);
if (m_artist.albums.has_value()) {
return QString::fromStdString(m_artist.albums.value().results[albumIndex].title);
}
break;
} else {
return QString::fromStdString(m_artist.songs.value().results[index.row()].title);
}
case Type:
return std::visit([&](auto&& item) {
using T = std::decay_t<decltype(item)>;
if constexpr(std::is_same_v<T, artist::Artist::Album>) {
return Type::Album;
} else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
return Type::Single;
} else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
return Type::Song;
} else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
return Type::Video;
}
if (index.row() >= countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles)) {
Q_UNREACHABLE();
}, m_view[index.row()]);
case Artists:
return QVariant::fromValue(std::vector<meta::Artist> {
{
m_artist.name,
m_artist.channel_id
if (m_artist.videos.has_value()) {
return Video;
}
});
case VideoId:
return std::visit([&](auto&& item) {
using T = std::decay_t<decltype(item)>;
if constexpr(std::is_same_v<T, artist::Artist::Album>) {
return QVariant();
} else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
return QVariant();
} else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
return QVariant(QString::fromStdString(item.video_id));
} else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
return QVariant(QString::fromStdString(item.video_id));
break;
} else if (index.row() >= countItems(m_artist.songs) + countItems(m_artist.albums)) {
if (m_artist.singles.has_value()) {
return Single;
}
Q_UNREACHABLE();
}, m_view[index.row()]);
case ThumbnailUrl:
return std::visit([&](auto&& item) {
if (!item.thumbnails.empty()) {
return QString::fromStdString(item.thumbnails.front().url);
break;
} else if (index.row() >= countItems(m_artist.songs)) {
if (m_artist.albums.has_value()) {
return Album;
}
return QString();
}, m_view[index.row()]);
break;
} else {
return Song;
}
}
Q_UNREACHABLE();
@@ -135,10 +116,7 @@ QHash<int, QByteArray> ArtistModel::roleNames() const
{
return {
{Title, "title"},
{Type, "type"},
{Artists, "artists"},
{VideoId, "videoId"},
{ThumbnailUrl, "thumbnailUrl"}
{Type, "type"}
};
}
@@ -167,25 +145,43 @@ QUrl ArtistModel::thumbnailUrl() const
return QUrl(QString::fromStdString(m_artist.thumbnails.back().url));
}
QUrl ArtistModel::webUrl() const
bool ArtistModel::loading() const
{
return QUrl(YTMUSIC_WEB_BASE_URL % u"channel/" % QString::fromStdString(m_artist.channel_id));
return m_loading;
}
void ArtistModel::setLoading(bool loading)
{
m_loading = loading;
Q_EMIT loadingChanged();
}
void ArtistModel::triggerItem(int row)
{
std::visit([&](auto&& item) {
using T = std::decay_t<decltype(item)>;
if constexpr(std::is_same_v<T, artist::Artist::Album>) {
Q_EMIT openAlbum(QString::fromStdString(item.browse_id));
} else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
Q_EMIT openAlbum(QString::fromStdString(item.browse_id));
} else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
Q_EMIT openSong(QString::fromStdString(item.video_id));
} else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
Q_EMIT openVideo(QString::fromStdString(item.video_id), QString::fromStdString(item.title));
} else {
Q_UNREACHABLE();
if (row >= countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles)) {
int videoIndex = row - (countItems(m_artist.songs)
+ countItems(m_artist.albums)
+ countItems(m_artist.singles));
if (m_artist.videos.has_value()) {
Q_EMIT openVideo(QString::fromStdString(m_artist.videos.value().results[videoIndex].video_id));
}
}, m_view[row]);
} else if (row >= countItems(m_artist.songs) + countItems(m_artist.albums)) {
int singleIndex = row - (countItems(m_artist.songs) + countItems(m_artist.albums));
if (m_artist.singles.has_value()) {
Q_EMIT openAlbum(QString::fromStdString(m_artist.singles.value().results[singleIndex].browse_id));
}
} else if (row >= countItems(m_artist.songs)) {
int albumIndex = row - countItems(m_artist.songs);
if (m_artist.albums.has_value()) {
Q_EMIT openAlbum(QString::fromStdString(m_artist.albums.value().results[albumIndex].browseId));
}
} else {
Q_EMIT openSong(QString::fromStdString(m_artist.songs.value().results[row].video_id));
}
}
+9 -25
View File
@@ -1,21 +1,15 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include <QAbstractListModel>
#include <QUrl>
#include <optional>
#include <ytmusic.h>
#include "multiiterableview.h"
#include "abstractytmusicmodel.h"
class ArtistModel : public AbstractYTMusicModel
class ArtistModel : public QAbstractListModel
{
Q_OBJECT
@@ -23,7 +17,7 @@ class ArtistModel : public AbstractYTMusicModel
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(QUrl thumbnailUrl READ thumbnailUrl NOTIFY thumbnailUrlChanged)
Q_PROPERTY(QUrl webUrl READ webUrl NOTIFY webUrlChanged)
Q_PROPERTY(bool loading READ loading WRITE setLoading NOTIFY loadingChanged)
public:
enum Type {
@@ -36,10 +30,7 @@ public:
enum Role {
Title,
Type,
Artists,
VideoId,
ThumbnailUrl
Type
};
explicit ArtistModel(QObject *parent = nullptr);
@@ -58,26 +49,19 @@ public:
QUrl thumbnailUrl() const;
Q_SIGNAL void thumbnailUrlChanged();
QUrl webUrl() const;
Q_SIGNAL void webUrlChanged();
bool loading() const;
void setLoading(bool loading);
Q_SIGNAL void loadingChanged();
Q_INVOKABLE void triggerItem(int row);
Q_SIGNAL void openAlbum(const QString &browseId);
Q_SIGNAL void openSong(const QString &videoId);
Q_SIGNAL void openVideo(const QString &videoId, const QString &title);
Q_SIGNAL void openVideo(const QString &videoId);
private:
QString m_channelId;
bool m_loading = false;
artist::Artist m_artist;
std::vector<artist::Artist::Album> albums;
std::vector<artist::Artist::Single> singles;
std::vector<artist::Artist::Song> songs;
std::vector<artist::Artist::Video> videos;
MultiIterableView<
artist::Artist::Album, artist::Artist::Single, artist::Artist::Song, artist::Artist::Video
> m_view;
};
-309
View File
@@ -1,309 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "asyncdatabase.h"
#include <QDir>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QUrl>
#include <QStringBuilder>
#include <QVariant>
#include <QSqlResult>
#include <QSqlError>
#include <QLoggingCategory>
#define SCHAMA_MIGRATIONS_TABLE "__qt_schema_migrations"
Q_DECLARE_LOGGING_CATEGORY(asyncdatabase)
Q_LOGGING_CATEGORY(asyncdatabase, "asyncdatabase")
namespace asyncdatabase_private {
// migrations
void createInternalTable(QSqlDatabase &database) {
QSqlQuery query(QStringLiteral("create table if not exists " SCHAMA_MIGRATIONS_TABLE " ("
"version Text primary key not null, "
"run_on timestamp not null default current_timestamp)"), database);
if (!query.exec()) {
printSqlError(query);
}
}
void markMigrationRun(QSqlDatabase &database, const QString &name) {
qCDebug(asyncdatabase) << "Marking migration" << name << "as done.";
QSqlQuery query(database);
if (!query.prepare(QStringLiteral("insert into " SCHAMA_MIGRATIONS_TABLE " (version) values (:name)"))) {
printSqlError(query);
}
query.bindValue(QStringLiteral(":name"), QVariant(name));
if (!query.exec()) {
printSqlError(query);
}
}
bool checkMigrationAlreadyRun(QSqlDatabase &database, const QString &name) {
qCDebug(asyncdatabase) << "Checking whether migration" << name << "is already applied";
QSqlQuery query(database);
query.prepare(QStringLiteral("select count(*) from " SCHAMA_MIGRATIONS_TABLE " where version = :name"));
query.bindValue(QStringLiteral(":name"), QVariant(name));
query.exec();
query.next();
int count = query.value(0).toInt();
bool isApplied = count > 0;
if (isApplied) {
qCDebug(asyncdatabase) << "… yes";
} else {
qDebug(asyncdatabase) << "… no";
}
return isApplied;
}
void runDatabaseMigrations(QSqlDatabase &database, const QString &migrationDirectory)
{
createInternalTable(database);
QDir dir(migrationDirectory);
auto entries = dir.entryList(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot, QDir::SortFlag::Name);
for (const auto &entry : entries) {
QDir subdir(entry);
if (!checkMigrationAlreadyRun(database, subdir.dirName())) {
QFile file(migrationDirectory % QDir::separator() % entry % QDir::separator() % "up.sql");
if (!file.open(QFile::ReadOnly)) {
qCDebug(asyncdatabase) << "Failed to open migration file" << file.fileName();
}
qCDebug(asyncdatabase) << "Running migration" << subdir.dirName();
database.transaction();
// Hackish
const auto statements = file.readAll().split(';');
bool migrationSuccessful = true;
for (const QByteArray &statement : statements) {
const auto trimmedStatement = statement.trimmed();
QSqlQuery query(database);
if (!trimmedStatement.isEmpty()) {
qCDebug(asyncdatabase) << "Running" << trimmedStatement;
if (!query.prepare(trimmedStatement)) {
printSqlError(query);
} else {
bool success = query.exec();
migrationSuccessful &= success;
if (!success) {
printSqlError(query);
}
}
}
}
if (migrationSuccessful) {
database.commit();
markMigrationRun(database, subdir.dirName());
} else {
database.rollback();
}
}
}
qCDebug(asyncdatabase) << "Migrations finished";
}
struct AsyncSqlDatabasePrivate {
QSqlDatabase database;
};
// Internal asynchronous database class
QFuture<void> AsyncSqlDatabase::establishConnection(const DatabaseConfiguration &configuration)
{
return runAsync([=, this] {
d->database = QSqlDatabase::addDatabase(configuration.type());
if (configuration.databaseName()) {
d->database.setDatabaseName(*configuration.databaseName());
}
if (configuration.hostName()) {
d->database.setHostName(*configuration.hostName());
}
if (configuration.userName()) {
d->database.setUserName(*configuration.userName());
}
if (configuration.password()) {
d->database.setPassword(*configuration.password());
}
if (!d->database.open()) {
qCDebug(asyncdatabase) << "Failed to open database" << d->database.lastError().text();
if (configuration.databaseName()) {
qCDebug(asyncdatabase) << "Tried to use database" << *configuration.databaseName();
}
}
});
}
AsyncSqlDatabase::AsyncSqlDatabase()
: QObject()
, d(std::make_unique<AsyncSqlDatabasePrivate>())
{
}
Row AsyncSqlDatabase::retrieveRow(const QSqlQuery &query) {
Row row;
int i = 0;
while (true) {
if (query.isValid()) {
QVariant value = query.value(i);
if (value.isValid()) {
row.push_back(std::move(value));
i++;
} else {
break;
}
} else {
break;
}
}
return row;
}
Rows AsyncSqlDatabase::retrieveRows(QSqlQuery &query)
{
Rows rows;
while (query.next()) {
rows.push_back(retrieveRow(query));
}
return rows;
}
std::optional<Row> AsyncSqlDatabase::retrieveOptionalRow(QSqlQuery &query)
{
query.next();
if (query.isValid()) {
return retrieveRow(query);
} else {
return std::nullopt;
}
}
QSqlDatabase &AsyncSqlDatabase::db()
{
return d->database;
}
AsyncSqlDatabase::~AsyncSqlDatabase() = default;
void printSqlError(const QSqlQuery &query)
{
qCDebug(asyncdatabase) << "SQL error:" << query.lastError().text();
}
QSqlQuery prepareQuery(const QSqlDatabase &database, const QString &sqlQuery)
{
qCDebug(asyncdatabase) << "Running" << sqlQuery;
QSqlQuery query(database);
if (!query.prepare(sqlQuery)) {
printSqlError(query);
}
return query;
}
QSqlQuery runQuery(QSqlQuery &query)
{
if (!query.exec()) {
printSqlError(query);
}
return query;
}
}
struct DatabaseConfigurationPrivate : public QSharedData {
QString type;
std::optional<QString> hostName;
std::optional<QString> databaseName;
std::optional<QString> userName;
std::optional<QString> password;
};
DatabaseConfiguration::DatabaseConfiguration() : d(new DatabaseConfigurationPrivate)
{}
DatabaseConfiguration::~DatabaseConfiguration() = default;
DatabaseConfiguration::DatabaseConfiguration(const DatabaseConfiguration &) = default;
void DatabaseConfiguration::setType(const QString &type) {
d->type = type;
}
const QString &DatabaseConfiguration::type() const {
return d->type;
}
void DatabaseConfiguration::setHostName(const QString &hostName) {
d->hostName = hostName;
}
const std::optional<QString> &DatabaseConfiguration::hostName() const {
return d->hostName;
}
void DatabaseConfiguration::setDatabaseName(const QString &databaseName) {
d->databaseName = databaseName;
}
const std::optional<QString> &DatabaseConfiguration::databaseName() const {
return d->databaseName;
}
void DatabaseConfiguration::setUserName(const QString &userName) {
d->userName = userName;
}
const std::optional<QString> &DatabaseConfiguration::userName() const {
return d->userName;
}
void DatabaseConfiguration::setPassword(const QString &password) {
d->password = password;
}
const std::optional<QString> &DatabaseConfiguration::password() const {
return d->password;
}
struct ThreadedDatabasePrivate {
asyncdatabase_private::AsyncSqlDatabase db;
};
std::unique_ptr<ThreadedDatabase> ThreadedDatabase::establishConnection(DatabaseConfiguration config) {
auto threadedDb = std::make_unique<ThreadedDatabase>();
threadedDb->setObjectName(QStringLiteral("database thread"));
threadedDb->d->db.moveToThread(&*threadedDb);
threadedDb->start();
threadedDb->d->db.establishConnection(config);
return threadedDb;
}
ThreadedDatabase::ThreadedDatabase()
: QThread()
, d(std::make_unique<ThreadedDatabasePrivate>())
{
}
ThreadedDatabase::~ThreadedDatabase()
{
quit();
wait();
}
asyncdatabase_private::AsyncSqlDatabase &ThreadedDatabase::db()
{
return d->db;
}
-152
View File
@@ -1,152 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
class QUrl;
class QSqlDatabase;
#include <QString>
#include <QObject>
#include <QFuture>
#include <QSharedDataPointer>
#include <memory>
#include <optional>
#include <tuple>
#include <vector>
#include "asyncdatabase_p.h"
struct DatabaseConfigurationPrivate;
///
/// Options for connecting to a database
///
class DatabaseConfiguration {
public:
DatabaseConfiguration();
DatabaseConfiguration(const DatabaseConfiguration &);
~DatabaseConfiguration();
/// Set the name of the database driver, for example DATABASE_TYPE_SQLITE or a custom string.
void setType(const QString &type);
const QString &type() const;
/// Set the hostname
void setHostName(const QString &hostName);
const std::optional<QString> &hostName() const;
/// Set the name of the database (path of the file for SQLite)
void setDatabaseName(const QString &databaseName);
const std::optional<QString> &databaseName() const;
/// Set user name
void setUserName(const QString &userName);
const std::optional<QString> &userName() const;
/// Set password
void setPassword(const QString &password);
const std::optional<QString> &password() const;
private:
QSharedDataPointer<DatabaseConfigurationPrivate> d;
};
///
/// The SQLite database driver
///
const QString DATABASE_TYPE_SQLITE = QStringLiteral("QSQLITE");
template <typename T>
concept FromSql = requires(T v, typename T::ColumnTypes row)
{
typename T::ColumnTypes;
{ std::tuple(row) } -> std::same_as<typename T::ColumnTypes>;
{ T::fromSql(row) } -> std::same_as<T>;
};
struct ThreadedDatabasePrivate;
class ThreadedDatabase : public QThread {
public:
///
/// \brief Connect to a database
/// \param configuration of the database connection
/// \return
///
static std::unique_ptr<ThreadedDatabase> establishConnection(DatabaseConfiguration config);
///
/// \brief Execute an SQL query on the database, ignoring the result.
/// \param SQL query string to execute
/// \param Parameters to bind to the placeholders in the SQL Query
/// \return
///
template <typename ...Args>
requires std::conjunction_v<std::is_convertible<Args, QVariant>...>
auto execute(const QString &sqlQuery, Args... args) -> QFuture<void> {
return db().execute(sqlQuery, args...);
}
///
/// Run database migrations in the given directory.
/// The directory needs to contain a subdirectory for each migration.
/// The subdirectories need to be named so that when sorted alphabetically the migrations will be run in the correct order.
/// Each subdirectory needs to contain a file named up.sql.
///
/// \param Directory which contains the migrations.
/// \return
///
auto runMigrations(const QString &migrationDirectory) -> QFuture<void> {
return db().runMigrations(migrationDirectory);
}
///
/// \brief Execute an SQL query on the database, retrieving the result.
/// \param SQL Query to execute
/// \param parameters to bind to the placeholders in the SQL query.
/// \return Future of a list of lists of variants.
///
/// T must provide a tuple of the column types as `using ColumnTypes = std::tuple<...>`
/// and a `static T fromSql(ColumnTypes tuple)` deserialization method.
///
template <typename T, typename ...Args>
requires FromSql<T> && std::conjunction_v<std::is_convertible<Args, QVariant>...>
auto getResults(const QString &sqlQuery, Args... args) -> QFuture<std::vector<T>> {
return db().getResults<T, Args...>(sqlQuery, args...);
}
///
/// \brief Like getResults, but for retrieving just one row.
///
template <typename T, typename ...Args>
requires FromSql<T> && std::conjunction_v<std::is_convertible<Args, QVariant>...>
auto getResult(const QString &sqlQuery, Args... args) -> QFuture<std::optional<T>> {
return db().getResult<T, Args...>(sqlQuery, args...);
}
ThreadedDatabase();
~ThreadedDatabase();
private:
asyncdatabase_private::AsyncSqlDatabase &db();
std::unique_ptr<ThreadedDatabasePrivate> d;
};
///
/// \brief Deserialize just a single value from a query result.
///
template <typename T>
struct SingleValue {
using ColumnTypes = std::tuple<T>;
static SingleValue fromSql(ColumnTypes tuple) {
auto [value] = tuple;
return SingleValue { value };
}
T value;
};
-179
View File
@@ -1,179 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <memory>
#include <tuple>
#include <optional>
#include <QFuture>
#include <QFutureWatcher>
#include <QSqlQuery>
#include <QSqlDatabase>
#include <QThread>
class DatabaseConfiguration;
namespace asyncdatabase_private {
// Helpers
template <typename Tuple, typename Func, std::size_t i>
inline constexpr void iterate_impl(Tuple &tup, Func fun)
{
if constexpr(i >= std::tuple_size_v<std::decay_t<decltype(tup)>>) {
return;
} else {
fun(std::get<i>(tup));
return asyncdatabase_private::iterate_impl<Tuple, Func, i + 1>(tup, fun);
}
}
template <typename Tuple, typename Func>
inline constexpr void iterate_tuple(Tuple &tup, Func fun)
{
asyncdatabase_private::iterate_impl<Tuple, Func, 0>(tup, fun);
}
template <typename T, typename QObjectDerivedType, typename Function>
void connectFuture(const QFuture<T> &future, QObjectDerivedType *self, const Function &fun) {
auto watcher = std::make_shared<QFutureWatcher<T>>();
watcher->setFuture(future);
QObject::connect(watcher.get(), &QFutureWatcherBase::finished, self, [self, watcher, fun, future] {
if constexpr (std::is_same_v<void, T>) {
if constexpr (std::is_member_function_pointer_v<Function>) {
fun->*(self);
} else {
fun();
}
} else if (future.resultCount() > 0) {
if constexpr (std::is_member_function_pointer_v<Function>) {
(self->*fun)(watcher->result());
} else {
fun(watcher->result());
}
}
});
}
using Row = std::vector<QVariant>;
using Rows = std::vector<Row>;
template <typename RowTypesTuple>
auto parseRow(const Row &row) -> RowTypesTuple
{
auto tuple = RowTypesTuple();
int i = 0;
asyncdatabase_private::iterate_tuple(tuple, [&](auto &elem) {
elem = row.at(i).value<std::decay_t<decltype(elem)>>();
i++;
});
return tuple;
}
template <typename RowTypesTuple>
auto parseRows(const Rows &rows) -> std::vector<RowTypesTuple> {
std::vector<RowTypesTuple> parsedRows;
parsedRows.reserve(rows.size());
std::ranges::transform(rows, std::back_inserter(parsedRows), parseRow<RowTypesTuple>);
return parsedRows;
}
void runDatabaseMigrations(QSqlDatabase &database, const QString &migrationDirectory);
void printSqlError(const QSqlQuery &query);
// non-template helper functions to allow patching a much as possible in the shared library
QSqlQuery prepareQuery(const QSqlDatabase &database, const QString &sqlQuery);
QSqlQuery runQuery(QSqlQuery &query);
struct AsyncSqlDatabasePrivate;
class AsyncSqlDatabase : public QObject {
Q_OBJECT
public:
QFuture<void> establishConnection(const DatabaseConfiguration &configuration);
template <typename T, typename ...Args>
auto getResults(const QString &sqlQuery, Args... args) -> QFuture<std::vector<T>> {
return runAsync([=, this] {
auto query = executeQuery(sqlQuery, args...);
const auto rows = parseRows<typename T::ColumnTypes>(retrieveRows(query));
std::vector<T> deserializedRows;
std::ranges::transform(rows, std::back_inserter(deserializedRows), T::fromSql);
return deserializedRows;
});
}
template <typename T, typename ...Args>
auto getResult(const QString &sqlQuery, Args... args) -> QFuture<std::optional<T>> {
return runAsync([=, this]() -> std::optional<T> {
auto query = executeQuery(sqlQuery, args...);
if (const auto row = retrieveOptionalRow(query)) {
return T::fromSql(parseRow<typename T::ColumnTypes>(*row));
}
return std::nullopt;
});
}
template <typename ...Args>
auto execute(const QString &sqlQuery, Args... args) -> QFuture<void> {
return runAsync([=, this] {
executeQuery(sqlQuery, args...);
});
}
auto runMigrations(const QString &migrationDirectory) -> QFuture<void> {
return runAsync([=, this] {
runDatabaseMigrations(db(), migrationDirectory);
});
}
AsyncSqlDatabase();
~AsyncSqlDatabase();
private:
template <typename ...Args>
QSqlQuery executeQuery(const QString &sqlQuery, Args... args) {
auto query = prepareQuery(db(), sqlQuery);
auto argsTuple = std::make_tuple<Args...>(std::move(args)...);
int i = 0;
asyncdatabase_private::iterate_tuple(argsTuple, [&](auto &arg) {
query.bindValue(i, arg);
i++;
});
return runQuery(query);
}
template <typename Functor>
QFuture<std::invoke_result_t<Functor>> runAsync(Functor func) {
using ReturnType = std::invoke_result_t<Functor>;
auto interface = std::make_shared<QFutureInterface<ReturnType>>();
QMetaObject::invokeMethod(this, [interface, func] {
if constexpr (!std::is_same_v<ReturnType, void>) {
auto result = func();
interface->reportResult(result);
} else {
func();
}
interface->reportFinished();
});
return interface->future();
}
Row retrieveRow(const QSqlQuery &query);
Rows retrieveRows(QSqlQuery &query);
std::optional<Row> retrieveOptionalRow(QSqlQuery &query);
QSqlDatabase &db();
std::unique_ptr<AsyncSqlDatabasePrivate> d;
};
}
+99 -102
View File
@@ -1,41 +1,22 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include "asyncytmusic.h"
#include <QThread>
#include <exception>
#include <QDebug>
#include <QJsonObject>
#include <QJsonArray>
#include <QFutureInterface>
#include <QCoreApplication>
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <ranges>
#include <type_traits>
#include <memory>
template <typename T, typename OP>
std::optional<std::invoke_result_t<OP, T>> mapOptional(const std::optional<T> &optional, OP op) {
if (optional.has_value()) {
if constexpr (std::is_member_function_pointer<OP>::value) {
return (&optional.value()->*op)();
} else {
return op(optional.value());
}
}
return std::nullopt;
}
static QThread *ytmthread = []() -> QThread * {
auto *thread = new QThread();
thread->setObjectName("YTMusicAPI");
return thread;
}();
AsyncYTMusic::AsyncYTMusic(QObject *parent)
: QObject(parent)
@@ -49,132 +30,148 @@ AsyncYTMusic::AsyncYTMusic(QObject *parent)
qRegisterMetaType<video_info::VideoInfo>();
qRegisterMetaType<watch::Playlist>();
qRegisterMetaType<std::optional<QString>>();
qRegisterMetaType<std::vector<meta::Artist>>();
qRegisterMetaType<meta::Artist>();
connect(this, &AsyncYTMusic::errorOccurred, this, [](const QString &err) {
std::cerr << qPrintable(err);
});
connect(this, &AsyncYTMusic::startSearch, this, &AsyncYTMusic::internalSearch);
connect(this, &AsyncYTMusic::startFetchArtist, this, &AsyncYTMusic::internalFetchArtist);
connect(this, &AsyncYTMusic::startFetchAlbum, this, &AsyncYTMusic::internalFetchAlbum);
connect(this, &AsyncYTMusic::startFetchSong, this, &AsyncYTMusic::internalFetchSong);
connect(this, &AsyncYTMusic::startFetchPlaylist, this, &AsyncYTMusic::internalFetchPlaylist);
connect(this, &AsyncYTMusic::startFetchArtistAlbums, this, &AsyncYTMusic::internalFetchArtistAlbums);
connect(this, &AsyncYTMusic::startExtractVideoInfo, this, &AsyncYTMusic::internalExtractVideoInfo);
connect(this, &AsyncYTMusic::startFetchWatchPlaylist, this, &AsyncYTMusic::internalFetchWatchPlaylist);
}
AsyncYTMusic &AsyncYTMusic::instance()
{
static AsyncYTMusic &inst = []() -> AsyncYTMusic& {
static AsyncYTMusic ytm;
ytm.moveToThread(ytmthread);
ytmthread->start();
return ytm;
}();
return inst;
}
void AsyncYTMusic::stopInstance()
{
ytmthread->quit();
ytmthread->wait();
}
//
// search
//
QFuture<std::vector<search::SearchResultItem>> AsyncYTMusic::search(const QString &query)
void AsyncYTMusic::search(const QString &query)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->search(query.toStdString());
});
Q_EMIT startSearch(query);
}
void AsyncYTMusic::internalSearch(const QString &query)
{
Q_EMIT searchFinished(m_ytdl.search(query.toStdString()));
}
//
// fetchArtist
//
QFuture<artist::Artist> AsyncYTMusic::fetchArtist(const QString &channelId)
void AsyncYTMusic::fetchArtist(const QString &channelId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_artist(channelId.toStdString());
});
Q_EMIT startFetchArtist(channelId);
}
void AsyncYTMusic::internalFetchArtist(const QString &channelId)
{
Q_EMIT fetchArtistFinished(m_ytdl.get_artist(channelId.toStdString()));
}
//
// fetchAlbum
//
QFuture<album::Album> AsyncYTMusic::fetchAlbum(const QString &browseId)
void AsyncYTMusic::fetchAlbum(const QString &browseId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_album(browseId.toStdString());
});
Q_EMIT startFetchAlbum(browseId);
}
void AsyncYTMusic::internalFetchAlbum(const QString &browseId)
{
Q_EMIT fetchAlbumFinished(m_ytdl.get_album(browseId.toStdString()));
}
//
// fetchSong
//
QFuture<std::optional<song::Song>> AsyncYTMusic::fetchSong(const QString &videoId)
void AsyncYTMusic::fetchSong(const QString &videoId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_song(videoId.toStdString());
});
Q_EMIT startFetchSong(videoId);
}
void AsyncYTMusic::internalFetchSong(const QString &videoId)
{
Q_EMIT fetchSongFinished(m_ytdl.get_song(videoId.toStdString()));
}
//
// fetchPlaylist
//
QFuture<playlist::Playlist> AsyncYTMusic::fetchPlaylist(const QString &playlistId) {
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_playlist(playlistId.toStdString());
});
void AsyncYTMusic::fetchPlaylist(const QString &playlistId) {
Q_EMIT startFetchPlaylist(playlistId);
}
void AsyncYTMusic::internalFetchPlaylist(const QString &playlistId)
{
Q_EMIT fetchPlaylistFinished(m_ytdl.get_playlist(playlistId.toStdString()));
}
//
// fetchArtistAlbum
//
QFuture<std::vector<artist::Artist::Album>> AsyncYTMusic::fetchArtistAlbums(const QString &channelId, const QString &params)
void AsyncYTMusic::fetchArtistAlbums(const QString &channelId, const QString &params)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_artist_albums(channelId.toStdString(), params.toStdString());
});
Q_EMIT startFetchArtistAlbums(channelId, params);
}
void AsyncYTMusic::internalFetchArtistAlbums(const QString &channelid, const QString &params)
{
Q_EMIT fetchArtistAlbumsFinished(m_ytdl.get_artist_albums(channelid.toStdString(), params.toStdString()));
}
//
// extractVideoInfo
//
QFuture<video_info::VideoInfo> AsyncYTMusic::extractVideoInfo(const QString &videoId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->extract_video_info(videoId.toStdString());
});
void AsyncYTMusic::extractVideoInfo(const QString &videoId) {
qDebug() << "extract video " << videoId;
Q_EMIT startExtractVideoInfo(videoId);
}
void AsyncYTMusic::internalExtractVideoInfo(const QString &videoId) {
Q_EMIT extractVideoInfoFinished(m_ytdl.extract_video_info(videoId.toStdString()));
}
//
// fetchWatchPlaylist
//
QFuture<watch::Playlist> AsyncYTMusic::fetchWatchPlaylist(const std::optional<QString> &videoId, const std::optional<QString> &playlistId)
void AsyncYTMusic::fetchWatchPlaylist(const std::optional<QString> &videoId, const std::optional<QString> &playlistId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_watch_playlist(
mapOptional(videoId, &QString::toStdString),
mapOptional(playlistId, &QString::toStdString)
);
});
Q_EMIT startFetchWatchPlaylist(videoId, playlistId);
}
QFuture<Lyrics> AsyncYTMusic::fetchLyrics(const QString &browseId)
void AsyncYTMusic::internalFetchWatchPlaylist(const std::optional<QString> &videoId, const std::optional<QString> &playlistId)
{
return invokeAndCatchOnThread([=, this]() {
return m_ytm->get_lyrics(
browseId.toStdString()
);
});
}
Q_EMIT fetchWatchPlaylistFinished(m_ytdl.get_watch_playlist(
[&]() -> std::optional<std::string> {
if (!videoId.has_value()) {
return std::nullopt;
}
YTMusicThread &YTMusicThread::instance()
{
static YTMusicThread thread;
return thread;
}
return videoId->toStdString();
}(),
[&]() -> std::optional<std::string> {
if (!playlistId.has_value()) {
return std::nullopt;
}
YTMusicThread::~YTMusicThread()
{
quit();
wait();
}
AsyncYTMusic *YTMusicThread::operator->()
{
return m_ytm;
}
AsyncYTMusic &YTMusicThread::get()
{
return *m_ytm;
}
YTMusicThread::YTMusicThread()
: m_ytm(new AsyncYTMusic())
{
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, m_ytm, &QObject::deleteLater);
setObjectName(QStringLiteral("YTMusicAPI"));
m_ytm->moveToThread(this);
start();
return playlistId->toStdString();
}()));
}
+48 -120
View File
@@ -1,20 +1,16 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QObject>
#include <QThread>
#include <QFuture>
#include <QFutureWatcher>
#include <vector>
#include <ytmusic.h>
constexpr QStringView YTMUSIC_WEB_BASE_URL = u"https://music.youtube.com/";
Q_DECLARE_METATYPE(std::vector<artist::Artist::Album>);
Q_DECLARE_METATYPE(std::vector<search::SearchResultItem>)
Q_DECLARE_METATYPE(artist::Artist)
@@ -24,143 +20,75 @@ Q_DECLARE_METATYPE(playlist::Playlist)
Q_DECLARE_METATYPE(video_info::VideoInfo)
Q_DECLARE_METATYPE(watch::Playlist)
Q_DECLARE_METATYPE(std::optional<QString>)
Q_DECLARE_METATYPE(std::vector<meta::Artist>)
Q_DECLARE_METATYPE(meta::Artist)
///
/// Lazy initialized unique_ptr
///
template <typename T>
class Lazy {
public:
T *operator->() {
return get().operator->();
}
inline std::unique_ptr<T> &get() {
if (!m_item) {
m_item = std::make_unique<T>();
}
Q_ASSERT(m_item);
return m_item;
}
private:
std::unique_ptr<T> m_item = nullptr;
};
///
/// Similar to QObject::connect, except that the first two arguments are replaced with a QFuture.
///
template <typename T, typename QObjectDerivedType, typename Function>
void connectFuture(const QFuture<T> &future, QObjectDerivedType *self, const Function &fun) {
auto watcher = std::make_shared<QFutureWatcher<T>>();
watcher->setFuture(future);
QObject::connect(watcher.get(), &QFutureWatcherBase::finished, self, [self, watcher, fun, future]() {
if constexpr (std::is_same_v<void, T>) {
if constexpr (std::is_member_function_pointer_v<Function>) {
(self->*fun)();
} else {
Q_UNUSED(self);
fun();
}
} else if (future.resultCount() > 0) {
if constexpr (std::is_member_function_pointer_v<Function>) {
(self->*fun)(watcher->result());
} else {
Q_UNUSED(self);
fun(watcher->result());
}
}
});
}
///
/// Applies a function to a the contained value of a QFuture once it finishes.
/// @returns a QFuture that finishes once the future given as first argument finished
///
template <typename T, typename Func>
QFuture<std::invoke_result_t<Func, T>> mapFuture(const QFuture<T> &future, Func mapFunction) {
using ReturnType = std::invoke_result_t<Func, T>;
auto watcher = std::make_shared<QFutureWatcher<T>>();
watcher->setFuture(future);
auto interface = std::make_shared<QFutureInterface<ReturnType>>();
QObject::connect(watcher.get(), &QFutureWatcherBase::finished, watcher.get(), [interface, watcher, mapFunction] {
auto result = watcher->result();
auto mapped = mapFunction(std::move(result));
interface->reportResult(mapped);
interface->reportFinished();
});
return interface->future();
}
class AsyncYTMusic : public QObject
{
friend class YTMusicThread;
Q_OBJECT
public:
explicit AsyncYTMusic(QObject *parent = nullptr);
///
/// Singleton instance of AsyncYtMusic running on it's own thread.
///
/// It is necessary t call stopInstance before the application exits,
/// so the thread can properly finish.
///
static AsyncYTMusic &instance();
static void stopInstance();
// public functions need to be thread safe
QFuture<std::vector<search::SearchResultItem>> search(const QString &query);
void search(const QString &query);
Q_SIGNAL void searchFinished(std::vector<search::SearchResultItem>);
QFuture<artist::Artist> fetchArtist(const QString &channelId);
void fetchArtist(const QString &channelId);
Q_SIGNAL void fetchArtistFinished(artist::Artist);
QFuture<album::Album> fetchAlbum(const QString &browseId);
void fetchAlbum(const QString &browseId);
Q_SIGNAL void fetchAlbumFinished(album::Album);
QFuture<std::optional<song::Song> > fetchSong(const QString &videoId);
void fetchSong(const QString &videoId);
Q_SIGNAL void fetchSongFinished(song::Song);
QFuture<playlist::Playlist> fetchPlaylist(const QString &playlistId);
void fetchPlaylist(const QString &playlistId);
Q_SIGNAL void fetchPlaylistFinished(playlist::Playlist);
QFuture<std::vector<artist::Artist::Album>> fetchArtistAlbums(const QString &channelId, const QString &params);
void fetchArtistAlbums(const QString &channelId, const QString &params);
Q_SIGNAL void fetchArtistAlbumsFinished(std::vector<artist::Artist::Album>);
QFuture<video_info::VideoInfo> extractVideoInfo(const QString &videoId);
void extractVideoInfo(const QString &videoId);
Q_SIGNAL void extractVideoInfoFinished(video_info::VideoInfo);
QFuture<watch::Playlist> fetchWatchPlaylist(const std::optional<QString> &videoId = std::nullopt ,
void fetchWatchPlaylist(const std::optional<QString> &videoId = std::nullopt ,
const std::optional<QString> &playlistId = std::nullopt);
QFuture<Lyrics> fetchLyrics(const QString &browseId);
Q_SIGNAL void fetchWatchPlaylistFinished(watch::Playlist);
Q_SIGNAL void errorOccurred(const QString &error);
protected:
explicit AsyncYTMusic(QObject *parent = nullptr);
private:
/// Invokes the given function on the thread of the YTMusic object, and handles exceptions that occur while invoking it.
template <typename Func>
QFuture<std::invoke_result_t<Func>> invokeAndCatchOnThread(Func fun) {
using ReturnType = std::invoke_result_t<Func>;
auto interface = std::make_shared<QFutureInterface<ReturnType>>();
QMetaObject::invokeMethod(this, [=, this]() {
try {
ReturnType val = fun();
interface->reportResult(val);
interface->reportFinished();
} catch (const std::exception &err) {
interface->reportFinished();
Q_EMIT errorOccurred(QString::fromLocal8Bit(err.what()));
}
});
return interface->future();
}
Q_SIGNAL void startSearch(const QString &query);
Q_SLOT void internalSearch(const QString &query);
// Python interpreter will be initialized from the thread calling the methods
Lazy<YTMusic> m_ytm;
};
Q_SIGNAL void startFetchArtist(const QString &channelId);
Q_SLOT void internalFetchArtist(const QString &channelId);
class YTMusicThread : private QThread {
public:
static YTMusicThread &instance();
~YTMusicThread() override;
Q_SIGNAL void startFetchAlbum(const QString &browseId);
Q_SLOT void internalFetchAlbum(const QString &browseId);
AsyncYTMusic *operator->();
AsyncYTMusic &get();
Q_SIGNAL void startFetchSong(const QString &videoId);
Q_SLOT void internalFetchSong(const QString &videoId);
private:
YTMusicThread();
Q_SIGNAL void startFetchPlaylist(const QString &playlistId);
Q_SLOT void internalFetchPlaylist(const QString &playlistId);
AsyncYTMusic *m_ytm;
Q_SIGNAL void startFetchArtistAlbums(const QString &channelid, const QString &params);
Q_SLOT void internalFetchArtistAlbums(const QString &channelid, const QString &params);
Q_SIGNAL void startExtractVideoInfo(const QString &videoId);
Q_SLOT void internalExtractVideoInfo(const QString &videoId);
Q_SIGNAL void startFetchWatchPlaylist(const std::optional<QString> &videoId, const std::optional<QString> &playlistId);
Q_SLOT void internalFetchWatchPlaylist(const std::optional<QString> &videoId, const std::optional<QString> &playlistId);
YTMusic m_ytdl;
};
+18 -73
View File
@@ -1,8 +1,4 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick 2.1
import org.kde.kirigami 2.14 as Kirigami
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.3
@@ -13,83 +9,32 @@ Kirigami.ScrollablePage {
property alias browseId: albumModel.browseId
title: albumModel.title
ListView {
id: songList
header: ListHeader {
visibleActions: [
Kirigami.Action {
icon.name: "media-playback-start"
text: i18n("Play")
onTriggered: {
applicationWindow().playPlaylist(albumModel.playlistId)
}
},
Kirigami.Action {
icon.name: "media-playlist-shuffle"
text: i18n("Shuffle")
onTriggered: {
applicationWindow().playShufflePlaylist(albumModel.playlistId)
}
}
]
overflowActions: [
Kirigami.Action {
text: i18n("Open in Browser")
icon.name: "internet-services"
onTriggered: Qt.openUrlExternally(albumModel.webUrl)
}
]
title: albumModel.title
imageSourceURL: albumModel.thumbnailUrl
subtitle: i18n("Album • %1" , albumModel.artists)
width: songList.width
actions {
main: Kirigami.Action {
icon.name: "media-playback-start"
text: i18n("Play")
onTriggered: {
applicationWindow().playPlaylist(albumModel.playlistId)
}
}
}
reuseItems: true
ListView {
header: Kirigami.ItemViewHeader {
backgroundImage.source: albumModel.thumbnailUrl
title: albumModel.title
}
model: AlbumModel {
id: albumModel
}
SongMenu {
id: menu
}
delegate: Kirigami.SwipeListItem {
id: delegateItem
delegate: Kirigami.BasicListItem {
required property string title
required property string videoId
required property var artists
required property string thumbnailUrl
required property string artistsDisplayString
required property int index
RowLayout {
Layout.fillHeight: true
Controls.Label {
text: (index + 1)
font.bold: true
leftPadding: 5
color: Kirigami.Theme.disabledTextColor
Layout.preferredWidth: 30
}
Controls.Label {
Layout.fillWidth: true
text: title
elide: Qt.ElideRight
}
}
actions: [
Kirigami.Action {
icon.name: "view-more-horizontal-symbolic"
text: i18n("More")
onTriggered: menu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, delegateItem.artistsDisplayString)
}
]
onClicked: play(delegateItem.videoId)
icon: "emblem-music-symbolic"
text: title
onClicked: play(videoId)
}
Controls.BusyIndicator {
+17 -85
View File
@@ -1,8 +1,4 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick 2.1
import org.kde.kirigami 2.14 as Kirigami
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.3
@@ -11,46 +7,14 @@ import org.kde.ytmusic 1.0
Kirigami.ScrollablePage {
property alias channelId: artistModel.channelId
property string radioId
property string shuffleId
title: artistModel.title
SongMenu {
id: menu
}
ListView {
id: songList
header: ListHeader {
visibleActions: [
Kirigami.Action {
text: i18n("Radio")
icon.name: "radio"
onTriggered: playPlaylist(radioId)
},
Kirigami.Action {
text: i18n("Shuffle")
icon.name: "media-playlist-shuffle"
onTriggered: playPlaylist(shuffleId)
}
]
overflowActions: [
Kirigami.Action {
text: i18n("Open in Browser")
icon.name: "internet-services"
onTriggered: Qt.openUrlExternally(artistModel.webUrl)
}
]
header: Kirigami.ItemViewHeader {
backgroundImage.source: artistModel.thumbnailUrl
title: artistModel.title
imageSourceURL: artistModel.thumbnailUrl
subtitle: i18n("Artist")
rounded: true
width: songList.width
}
reuseItems: true
model: ArtistModel {
id: artistModel
@@ -64,19 +28,10 @@ Kirigami.ScrollablePage {
play(videoId)
}
onOpenVideo: (videoId, title) => {
videoPlayMessage.text = i18n("Video playback is not supported yet. Do you want to play only the audio of \"%1\"?", title)
videoPlayMessage.visible = true
videoPlayMessage.okAction = () => {
play(videoId)
videoPlayMessage.visible = false
}
videoPlayMessage.cancelAction = () => {
videoPlayMessage.visible = false
}
onOpenVideo: (videoId) => {
}
}
section.property: "type"
section.delegate: Kirigami.ListSectionHeader {
text: {
@@ -92,42 +47,23 @@ Kirigami.ScrollablePage {
}
}
}
delegate: Kirigami.SwipeListItem {
id: delegateItem
delegate: Kirigami.BasicListItem {
required property string title
required property int type
required property int index
required property var artists
required property string videoId
required property string thumbnailUrl
RowLayout {
Layout.fillHeight: true
RoundedImage {
source: delegateItem.thumbnailUrl
height: 35
width: height
radius:5
}
Controls.Label {
Layout.fillWidth: true
text: title
elide: Qt.ElideRight
text: title
icon: {
switch (type) {
case ArtistModel.Album:
return "media-album-cover"
case ArtistModel.Single:
case ArtistModel.Song:
return "emblem-music-symbolic"
case ArtistModel.Video:
return "emblem-videos-symbolic"
}
}
actions: [
Kirigami.Action {
icon.name: "view-more-horizontal-symbolic"
text: i18n("More")
visible: type === ArtistModel.Song
onTriggered: menu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, artistModel.title)
}
]
onClicked: artistModel.triggerItem(index)
}
@@ -136,8 +72,4 @@ Kirigami.ScrollablePage {
visible: artistModel.loading
}
}
footer: ConfirmationMessage {
id: videoPlayMessage
}
}
-26
View File
@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: 2021 Dimitris Kardarakos <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import org.kde.kirigami 2.14 as Kirigami
Kirigami.InlineMessage {
property var okAction
property var cancelAction
visible: false
actions: [
Kirigami.Action {
text: i18n("OK")
onTriggered: okAction()
},
Kirigami.Action {
text: i18n("Cancel")
onTriggered: cancelAction()
}
]
}
-312
View File
@@ -1,312 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.1
import QtQuick.Controls 2.12 as Controls
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.14 as Kirigami
import org.kde.ytmusic 1.0
Kirigami.ScrollablePage {
objectName: "libraryPage"
globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None
ColumnLayout {
RowLayout {
Layout.fillWidth: true
Kirigami.Heading {
text: i18n("Favourites")
Layout.alignment: Qt.AlignLeft
}
// Spacer
Item {
Layout.fillWidth: true
}
Controls.ToolButton {
text: i18n("Show All")
Layout.alignment: Qt.AlignRight
icon.name: "arrow-right"
onClicked: pageStack.layers.push("qrc:/PlaybackHistory.qml", {
"dataModel": Library.favourites,
"title": i18n("Favourite Songs")
})
}
}
Controls.ScrollView {
Layout.fillWidth: true
RowLayout {
spacing: 20
Repeater {
Layout.fillWidth: true
model: Library.favourites
delegate: ColumnLayout {
id: delegateItem
required property string title
required property string artists
required property string videoId
Layout.fillWidth: false
Layout.maximumWidth: 200
Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
id: favCover
MouseArea {
id: favArea
anchors.fill: parent
onClicked: play(delegateItem.videoId)
hoverEnabled: !Kirigami.Settings.hasTransientTouchInput
onEntered: {
if (!Kirigami.Settings.hasTransientTouchInput)
favSelected.visible = true
}
onExited: favSelected.visible = false
}
Layout.margins: 5
width: 200
height: 200
radius: 10
shadow.size: 15
shadow.xOffset: 5
shadow.yOffset: 5
shadow.color: Qt.rgba(0, 0, 0, 0.2)
ThumbnailSource {
id: thumbnailSource
videoId: delegateItem.videoId
}
RoundedImage {
source: thumbnailSource.cachedPath
height: 200
width: height
radius: 10
}
Rectangle {
id: favSelected
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.hoverColor
radius: 10
opacity: 0.2
}
Rectangle {
height: 45
width: 45
radius: 50
color: Kirigami.Theme.hoverColor
opacity: 0.8
anchors.centerIn: parent
}
Kirigami.Icon {
x: 100 - 0.43 * height
y: 100 - 0.5 * height
color: "white"
source: "media-playback-start"
}
visible: false
anchors.fill: parent
radius: 9
border.color: Kirigami.Theme.hoverColor
border.width: 2
color: "transparent"
}
}
RowLayout {
ColumnLayout {
Controls.Label {
text: delegateItem.title
Layout.maximumWidth: 200
Layout.fillWidth: true
leftPadding: 5
elide: Text.ElideRight
}
Controls.Label {
Layout.fillWidth: true
Layout.maximumWidth: 200
leftPadding: 5
color: Kirigami.Theme.disabledTextColor
text: delegateItem.artists
elide: Text.ElideRight
}
}
Controls.ToolButton {
Layout.fillHeight: true
icon.name: "view-more-horizontal-symbolic"
onPressed: favMenu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, delegateItem.artists)
SongMenu {
id:favMenu
}
}
}
Item {
height: 5
}
}
}
}
}
Item {
height: 20
}
RowLayout {
Layout.fillWidth: true
Kirigami.Heading {
text: i18n("Most played")
Layout.alignment: Qt.AlignLeft
}
// Spacer
Item {
Layout.fillWidth: true
}
Controls.ToolButton {
text: i18n("Show All")
Layout.alignment: Qt.AlignRight
icon.name: "arrow-right"
onClicked: pageStack.layers.push("qrc:/PlaybackHistory.qml", {
"dataModel": Library.playbackHistory,
"title": i18n("Played Songs")
})
}
}
Controls.ScrollView {
Layout.fillWidth: true
RowLayout {
spacing: 20
Repeater {
Layout.fillWidth: true
model: Library.mostPlayed
delegate: ColumnLayout {
id: mpdelegateItem
required property string title
required property string artists
required property string videoId
Layout.fillWidth: false
Layout.maximumWidth: 200
Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
id: recCover
MouseArea {
id: recArea
anchors.fill: parent
onClicked: play(mpdelegateItem.videoId)
hoverEnabled: !Kirigami.Settings.hasTransientTouchInput
onEntered: {
if (!Kirigami.Settings.hasTransientTouchInput)
recSelected.visible = true
}
onExited: recSelected.visible = false
}
Layout.margins: 5
width: 200
height: 200
radius: 10
shadow.size: 15
shadow.xOffset: 5
shadow.yOffset: 5
shadow.color: Qt.rgba(0, 0, 0, 0.2)
ThumbnailSource {
id: mpthumbnailSource
videoId: mpdelegateItem.videoId
}
RoundedImage {
source: mpthumbnailSource.cachedPath
height: 200
width: height
radius: 10
}
Rectangle {
id: recSelected
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.hoverColor
radius: 10
opacity: 0.2
}
Rectangle {
height: 45
width: 45
radius: 50
color: Kirigami.Theme.hoverColor
opacity: 0.8
anchors.centerIn: parent
}
Kirigami.Icon {
x: 100 - 0.43 * height
y: 100 - 0.5 * height
color: "white"
source: "media-playback-start"
}
visible: false
anchors.fill: parent
radius: 9
border.color: Kirigami.Theme.hoverColor
border.width: 2
color: "transparent"
}
}
RowLayout {
ColumnLayout {
Controls.Label {
text: mpdelegateItem.title
Layout.maximumWidth: 200
Layout.fillWidth: true
leftPadding: 5
elide: Text.ElideRight
}
Controls.Label {
Layout.fillWidth: true
Layout.maximumWidth: 200
leftPadding: 5
color: Kirigami.Theme.disabledTextColor
text: mpdelegateItem.artists
elide: Text.ElideRight
}
}
Controls.ToolButton {
Layout.fillHeight: true
icon.name: "view-more-horizontal-symbolic"
onPressed: mpMenu.openForSong(mpdelegateItem.videoId, mpdelegateItem.title, mpdelegateItem.artists, mpdelegateItem.artists)
SongMenu{
id:mpMenu
}
}
}
Item {
height: 5
}
}
}
}
}
}
}
-143
View File
@@ -1,143 +0,0 @@
// SPDX-FileCopyrightText: 2022 Mathis Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import org.kde.kirigami 2.14 as Kirigami
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import org.kde.ytmusic 1.0
Item {
id:root
required property string imageSourceURL
property bool rounded : false
required property string title
property string subtitle
property list<Kirigami.Action> visibleActions
property list<Kirigami.Action> overflowActions
height: root.width > 500 ? 200 : 400
Item {
anchors.fill: parent
Image {
scale: 1.8
anchors.fill: parent
asynchronous: true
source: imageSourceURL
fillMode: Image.PreserveAspectCrop
sourceSize.width: 512
sourceSize.height: 512
}
layer.enabled: true
layer.effect: HueSaturation {
cached: true
saturation: 1.9
layer.enabled: true
layer.effect: FastBlur {
cached: true
radius: 100
}
}
}
Rectangle {
anchors.fill: parent
gradient: Gradient{
GradientStop { position: -1.0; color: "transparent" }
GradientStop { position: 1.0; color: Kirigami.Theme.backgroundColor }
}
}
GridLayout {
width: parent.width
flow: root.width > 500?GridLayout.LeftToRight:GridLayout.TopToBottom
Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
Layout.margins: 30
width: 150
height: width
radius: rounded ? 100 : 10
shadow.size: 15
shadow.xOffset: 5
shadow.yOffset: 5
shadow.color: Qt.rgba(0, 0, 0, 0.2)
RoundedImage {
source: imageSourceURL
height: parent.height
width: height
radius: rounded ? 100 : 10
}
}
ColumnLayout {
Layout.leftMargin: 30
Layout.fillWidth: true
Controls.Label {
Layout.fillWidth: true
text: title
font.bold: true
font.pixelSize: 22
elide: Qt.ElideRight
}
Controls.Label {
Layout.fillWidth: true
text: subtitle
elide: Qt.ElideRight
}
RowLayout {
visible: root.visibleActions.length>0
Layout.topMargin: 30
Layout.rightMargin: 30
Repeater {
model: root.visibleActions
delegate: Controls.RoundButton{
required property var modelData
Layout.fillWidth: root.width <= 500
leftPadding: 20
rightPadding: 20
icon.name: modelData.icon.name
text:modelData.text
onClicked: modelData.triggered()
}
}
Controls.Menu {
id: overflowMenu
Instantiator {
model: root.overflowActions
onObjectAdded: overflowMenu.insertItem(index, object)
onObjectRemoved: overflowMenu.removeItem(object)
delegate: Controls.MenuItem {
required property var modelData
icon.name: modelData.icon.name
text:modelData.text
onTriggered: modelData.triggered()
}
}
}
Controls.RoundButton {
onClicked: overflowMenu.popup()
visible: root.overflowActions.length > 0
icon.name: "overflow-menu"
}
}
}
}
}
-506
View File
@@ -1,506 +0,0 @@
// SPDX-FileCopyrightText: 2020-2022 Devin Lin <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.0
import QtMultimedia 5.12
import org.kde.kirigami 2.19 as Kirigami
import org.kde.ytmusic 1.0
Item {
id: root
required property var info // VideoInfoExtractor object
required property var audio // Audio object
signal requestClose()
// background image
Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: Qt.rgba(25, 25, 30, 1)
}
Image {
scale: 1.8
anchors.fill: parent
asynchronous: true
source: info.thumbnail
fillMode: Image.PreserveAspectCrop
sourceSize.width: 512
sourceSize.height: 512
}
layer.enabled: true
layer.effect: HueSaturation {
cached: true
lightness: -0.5
saturation: 1.9
layer.enabled: true
layer.effect: FastBlur {
cached: true
radius: 100
}
}
}
Rectangle {
anchors.fill: parent
gradient: Gradient{
GradientStop { position: 0.0; color: "transparent" }
GradientStop { position: 1.1; color: "black" }
}
}
// content
ColumnLayout {
anchors.fill: parent
spacing: 0
// hide arrow button
ToolButton {
id: closeButton
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.maximumHeight: parent.height
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.maximumWidth: parent.height
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.topMargin: Kirigami.Units.smallSpacing
icon.name: "arrow-down"
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
onClicked: root.requestClose()
}
// top tab bar
RowLayout {
id: tabBar
Layout.alignment: Qt.AlignHCenter
ToolButton {
id: nowPlayingTab
padding: Kirigami.Units.largeSpacing
onClicked: swipeView.setCurrentIndex(0)
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
Layout.preferredWidth: Kirigami.Units.gridUnit * 9
contentItem: Label {
text: i18n("Now Playing")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
color: "white" // we need to ensure the label is white
}
// selection indicator
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Kirigami.Theme.highlightColor
height: 1
visible: swipeView.currentIndex == 0
}
}
ToolButton {
id: queueTab
padding: Kirigami.Units.largeSpacing
onClicked: swipeView.setCurrentIndex(1)
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
Layout.preferredWidth: Kirigami.Units.gridUnit * 9
contentItem: Label {
text: i18n("Queue")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
color: "white"
}
// selection indicator
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Kirigami.Theme.highlightColor
height: 1
visible: swipeView.currentIndex == 1
}
}
ToolButton {
id: lyricsTab
padding: Kirigami.Units.largeSpacing
onClicked: swipeView.setCurrentIndex(2)
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
Layout.preferredWidth: Kirigami.Units.gridUnit * 9
contentItem: Label {
text: i18n("Lyrics")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
color: "white"
}
// selection indicator
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Kirigami.Theme.highlightColor
height: 1
visible: swipeView.currentIndex == 2
}
}
}
// tabs
Item {
height: 30
}
SwipeView {
id: swipeView
property double specWidth: {
let allowedWidth = root.width - Kirigami.Units.largeSpacing * 4;
let allowedHeight = root.height - Kirigami.Units.largeSpacing * 8 - (closeButton.height + tabBar.height + bottomPlayerControls.height);
if (allowedWidth > allowedHeight) {
return allowedHeight;
} else {
return allowedWidth;
}
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.fillWidth: true
Layout.preferredHeight: specWidth
// music art
Item {
height: swipeView.width
width: swipeView.height
Kirigami.ShadowedRectangle {
anchors.centerIn: parent
width: swipeView.specWidth
height: swipeView.specWidth
color: "transparent"
radius: 10
shadow.size: 15
shadow.xOffset: 5
shadow.yOffset: 5
shadow.color: Qt.rgba(0, 0, 0, 0.2)
RoundedImage {
source: info.thumbnail
height: parent.height
width: height
radius: 10
}
}
}
// playlist
ColumnLayout{
width: swipeView.width
height: swipeView.height
ScrollView {
Layout.maximumWidth: 900
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
ListView {
clip: true
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
BusyIndicator {
anchors.centerIn: parent
visible: UserPlaylistModel.loading || UserPlaylistModel.loading
}
model: UserPlaylistModel
delegate: Kirigami.SwipeListItem {
id: delegateItem
required property string title
required property string videoId
required property string artists
required property bool isCurrent
backgroundColor: 'transparent'
highlighted: isCurrent
onClicked: UserPlaylistModel.skipTo(videoId)
ColumnLayout {
Layout.fillWidth: true
Kirigami.Heading {
elide: Text.ElideRight
Layout.fillWidth: true
level: 2
text: title
}
Label {
elide: Text.ElideRight
Layout.fillWidth: true
text: artists
}
}
actions: [
Kirigami.Action {
text: i18n("Remove Track")
icon.name: "list-remove"
icon.color: "white"
onTriggered: UserPlaylistModel.remove(delegateItem.videoId)
}
]
}
}
}
}
ColumnLayout{
width: swipeView.width
height: swipeView.height
ScrollView {
Layout.maximumWidth: 900
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
clip: true
//contentY: audio.position / audio.duration
Label {
padding: 20
text: UserPlaylistModel.lyrics
color: "white"
}
}
}
}
ColumnLayout {
id: bottomPlayerControls
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Kirigami.Units.gridUnit * 2
Layout.rightMargin: Kirigami.Units.gridUnit * 2
Layout.bottomMargin: Kirigami.Units.gridUnit * 2
// song name
Label {
id: mainLabel
text: info.title ? info.title : i18n("No media playing")
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
maximumLineCount: 1
// Hardcoded because the footerbar blur always makes a dark-ish
// background, so we don't want to use a color scheme color that
// might also be dark
color: "white"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.5
font.weight: Font.Bold
font.bold: true
}
// song artist
Kirigami.Heading {
id: authorLabel
text: info.artist ? info.artist : info.channel
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
maximumLineCount: 1
// Hardcoded because the footerbar blur always makes a dark-ish
// background, so we don't want to use a color scheme color that
// might also be dark
color: "white"
opacity: 0.9
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.3
font.weight: Font.Bold
font.bold: true
}
// slider row
RowLayout {
Layout.topMargin: Kirigami.Units.gridUnit * 2
spacing: Kirigami.Units.smallSpacing
Label {
Layout.alignment: Qt.AlignVCenter
color: "white"
visible: info.title
text: PlayerUtils.formatTimestamp(audio.position)
}
Slider {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
from: 0
to: audio.duration
value: audio.position
enabled: audio.seekable
onMoved: {
console.log("Value:", value);
audio.seek(Math.floor(value));
}
Behavior on value {
NumberAnimation {
duration: 1000
}
}
}
Label {
Layout.alignment: Qt.AlignVCenter
color: "white"
visible: info.title
text: PlayerUtils.formatTimestamp(audio.duration)
}
}
RowLayout {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
// ensure white icons
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
ToolButton {
id: favouriteButton
readonly property QtObject favouriteWatcher: Library.favouriteWatcher(info.videoId)
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Layout.maximumWidth: height
Layout.preferredWidth: height
onClicked: {
if (favouriteWatcher) {
if (favouriteWatcher.isFavourite) {
Library.removeFavourite(info.videoId)
// This would insert slightly ugly data into the database, but let's hope the song is already saved
} else {
let index = UserPlaylistModel.index(UserPlaylistModel.currentIndex, 0)
let videoId = UserPlaylistModel.data(index, UserPlaylistModel.VideoId)
let title = UserPlaylistModel.data(index, UserPlaylistModel.Title)
let artist = UserPlaylistModel.data(index, UserPlaylistModel.Artists)
let album = UserPlaylistModel.data(index, UserPlaylistModel.Album)
Library.addFavourite(videoId, title, artist, album)
}
}
}
icon.name: favouriteWatcher ? (favouriteWatcher.isFavourite ? "starred-symbolic" : "non-starred-symbolic") : ""
icon.width: Kirigami.Units.gridUnit * 1.5
icon.height: Kirigami.Units.gridUnit * 1.5
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
ToolButton {
id: skipBackwardButton
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Layout.maximumWidth: height
Layout.preferredWidth: height
enabled: false
icon.name: "media-skip-backward"
icon.width: Kirigami.Units.gridUnit * 1.5
icon.height: Kirigami.Units.gridUnit * 1.5
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
ToolButton {
id: playPauseButton
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 4
Layout.maximumWidth: height
Layout.preferredWidth: height
enabled: info.audioUrl != ""
onClicked: audio.playbackState === Audio.PlayingState ? audio.pause() : audio.play()
icon.name: audio.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
icon.width: Kirigami.Units.gridUnit * 3
icon.height: Kirigami.Units.gridUnit * 3
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
ToolButton {
id: skipForwardButton
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Layout.maximumWidth: height
Layout.preferredWidth: height
enabled: UserPlaylistModel.canSkip
onClicked: UserPlaylistModel.next()
icon.name: "media-skip-forward"
icon.width: Kirigami.Units.gridUnit * 1.5
icon.height: Kirigami.Units.gridUnit * 1.5
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
ToolButton {
id: shuffleButton
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Layout.maximumWidth: height
Layout.preferredWidth: height
onClicked: UserPlaylistModel.shufflePlaylist()
icon.name: "media-playlist-shuffle"
icon.width: Kirigami.Units.gridUnit * 1.5
icon.height: Kirigami.Units.gridUnit * 1.5
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
}
}
}
}
-212
View File
@@ -1,212 +0,0 @@
// SPDX-FileCopyrightText: 2020-2022 Devin Lin <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.0
import QtMultimedia 5.12
import org.kde.kirigami 2.19 as Kirigami
import org.kde.ytmusic 1.0
Item {
id: root
z: 2000
required property int progressBarHeight
required property int minimizedPlayerContentHeight
required property var info // VideoInfoExtractor object
required property var audio // Audio object
readonly property bool isWidescreen: width >= Kirigami.Units.gridUnit * 40
signal requestOpen()
Rectangle {
id: miniProgressBar
z: 1
visible: !root.isWidescreen
anchors.top: parent.top
anchors.left: parent.left
height: root.progressBarHeight
color: Kirigami.Theme.highlightColor
width: parent.width * (audio.position / audio.duration)
}
RowLayout {
anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.maximumWidth: Kirigami.Units.gridUnit * 20
Layout.preferredWidth: Kirigami.Units.gridUnit * 20
color: Qt.rgba(0, 0, 0, trackClick.containsMouse ? 0.1 : trackClick.pressed ? 0.3 : 0)
RowLayout {
anchors.fill: parent
anchors.leftMargin: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
// track image
Item {
property double imageSize: root.height - Kirigami.Units.largeSpacing * 2
Layout.alignment: Qt.AlignVCenter
Layout.maximumWidth: imageSize
Layout.preferredWidth: imageSize
Layout.maximumHeight: imageSize
Layout.minimumHeight: imageSize
RoundedImage {
id: mainIcon
anchors.fill: parent
visible: !loadingIndicator.visible
source: info.thumbnail
radius: 5
}
BusyIndicator {
id: loadingIndicator
anchors.centerIn: parent
visible: UserPlaylistModel.loading
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
}
// track information
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
id: mainLabel
text: info.title ? info.title : i18n("No media playing")
textFormat: Text.PlainText
wrapMode: Text.Wrap
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
maximumLineCount: 1
// Hardcoded because the footerbar blur always makes a dark-ish
// background, so we don't want to use a color scheme color that
// might also be dark
color: "white"
font.weight: Font.Bold
}
Label {
id: authorLabel
text: info.artist ? info.artist : info.channel
textFormat: Text.PlainText
wrapMode: Text.Wrap
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
maximumLineCount: 1
// Hardcoded because the footerbar blur always makes a dark-ish
// background, so we don't want to use a color scheme color that
// might also be dark
color: "white"
}
}
}
MouseArea {
id: trackClick
anchors.fill: parent
hoverEnabled: true
onClicked: root.requestOpen()
}
}
RowLayout {
visible: root.isWidescreen
Layout.preferredWidth: 0
Layout.fillHeight: true
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.smallSpacing
Label {
Layout.alignment: Qt.AlignVCenter
color: "white"
visible: info.title
text: PlayerUtils.formatTimestamp(audio.position)
}
Slider {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
from: 0
to: audio.duration
value: audio.position
enabled: audio.seekable
onMoved: {
console.log("Value:", value);
audio.seek(Math.floor(value));
}
Behavior on value {
NumberAnimation {
duration: 1000
}
}
}
Label {
Layout.alignment: Qt.AlignVCenter
color: "white"
visible: info.title
text: PlayerUtils.formatTimestamp(audio.duration)
}
}
ToolButton {
id: playPauseButton
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.maximumWidth: height
Layout.preferredWidth: height
enabled: info.audioUrl != ""
onClicked: audio.playbackState === Audio.PlayingState ? audio.pause() : audio.play()
icon.name: audio.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
icon.width: Kirigami.Units.gridUnit
icon.height: Kirigami.Units.gridUnit
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
ToolButton {
id: skipForwardButton
Layout.rightMargin: Math.floor(Kirigami.Units.smallSpacing / 2)
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.maximumWidth: height
Layout.preferredWidth: height
enabled: UserPlaylistModel.canSkip
onClicked: UserPlaylistModel.next()
icon.name: "media-skip-forward"
icon.width: Kirigami.Units.gridUnit
icon.height: Kirigami.Units.gridUnit
icon.color: "white"
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
}
}
}
-78
View File
@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 as Controls
import org.kde.kirigami 2.15 as Kirigami
import org.kde.ytmusic 1.0
Kirigami.ScrollablePage {
id: playbackHistoryPage
title: i18n("Unknown list of songs")
property QtObject dataModel
SongMenu {
id:menu
}
ListView {
id: listView
reuseItems: true
Kirigami.PlaceholderMessage {
visible: listView.count < 1
text: i18n("No songs here yet")
anchors.centerIn: parent
}
model: playbackHistoryPage.dataModel
delegate: Kirigami.SwipeListItem {
id: delegateItem
required property string title
required property string videoId
required property string artists
onClicked: {
play(videoId)
}
RowLayout {
ThumbnailSource {
id: thumbnailSource
videoId: delegateItem.videoId
}
RoundedImage {
source: thumbnailSource.cachedPath
height: 35
width: height
radius: 5
}
ColumnLayout {
Controls.Label {
text: delegateItem.title
Layout.fillWidth: true
}
Controls.Label {
Layout.fillWidth: true
color: Kirigami.Theme.disabledTextColor
text: delegateItem.artists
}
}
}
actions: [
Kirigami.Action {
icon.name: "view-more-horizontal-symbolic"
text: i18n("More")
onTriggered: menu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, delegateItem.artists)
}
]
}
}
}
+162 -404
View File
@@ -1,429 +1,187 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
// SPDX-FileCopyrightText: 2021 Bart De Vries <[email protected]>
// SPDX-FileCopyrightText: 2020-2022 Devin Lin <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.12 as Controls
import QtMultimedia 5.12
import QtGraphicalEffects 1.0
import org.kde.kirigami 2.12 as Kirigami
import org.kde.ytmusic 1.0
Flickable {
id: footerItem
import QtMultimedia 5.12
ColumnLayout {
id: footerLayout
// input only
property alias videoId: playlistModel.initialVideoId
property alias playlistId: playlistModel.playlistId
property bool maximized: false
readonly property int progressBarHeight: Kirigami.Units.gridUnit / 6
readonly property int minimizedPlayerContentHeight: Math.round(Kirigami.Units.gridUnit * 3.5)
readonly property int minimizedPlayerHeight: minimizedPlayerContentHeight + progressBarHeight
boundsBehavior: Flickable.StopAtBounds
function close() {
toClose.restart();
anchors.left: parent.left
anchors.right: parent.right
height: maximized ? applicationWindow().height : player.preferredHeight
Image {
Layout.fillWidth: true
Layout.fillHeight: true
source: info.thumbnail
}
function resetToBoundsOnFlick() {
if (!atYBeginning || !atYEnd) {
if (footerItem.verticalVelocity > 0) {
toOpen.restart();
} else if (footerItem.verticalVelocity < 0) {
toClose.restart();
} else { // i.e. when verticalVelocity === 0
if (contentY > contentHeight / 4) {
toOpen.restart();
} else {
toClose.restart();
}
Controls.ScrollView {
Layout.fillHeight: footerLayout.maximized
Layout.fillWidth: true
visible: footerLayout.maximized
ListView {
id: playlistView
anchors.fill: parent
model: PlaylistModel {
id: playlistModel
}
}
}
delegate: Kirigami.BasicListItem {
required property string title
required property string videoId
required property string artists
required property bool isCurrent
function resetToBoundsOnResize() {
if (contentY > contentHeight / 4) {
contentY = contentHeight / 2;
} else {
contentY = 0;
}
}
NumberAnimation on contentY {
id: toOpen
from: contentY
to: contentHeight / 2
duration: Kirigami.Units.longDuration * 2
easing.type: Easing.OutCubic
running: false
}
NumberAnimation on contentY {
id: toClose
from: contentY
to: 0
duration: Kirigami.Units.longDuration * 2
easing.type: Easing.OutCubic
running: false
}
highlighted: isCurrent
// snap to end
MouseArea {
anchors.fill: footerLayout
propagateComposedEvents: true
onPressed: {
toOpen.stop();
toClose.stop();
propagateComposedEvents = true;
}
onReleased: footerItem.resetToBoundsOnFlick()
}
onClicked: playlistModel.skipTo(videoId)
onMovementStarted: {
toOpen.stop();
toClose.stop();
}
onFlickStarted: resetToBoundsOnFlick()
onMovementEnded: resetToBoundsOnFlick()
onHeightChanged: resetToBoundsOnResize()
property var videoInfoExtractor: VideoInfoExtractor {
id: info
onSongChanged: audio.play()
videoId: UserPlaylistModel.currentVideoId
onTitleChanged: {
let index = UserPlaylistModel.index(UserPlaylistModel.currentIndex, 0)
let videoId = UserPlaylistModel.data(index, UserPlaylistModel.VideoId)
let title = UserPlaylistModel.data(index, UserPlaylistModel.Title)
let artist = UserPlaylistModel.data(index, UserPlaylistModel.Artists)
let album = UserPlaylistModel.data(index, UserPlaylistModel.Album)
Library.addPlaybackHistoryItem(videoId, title, artist, album)
}
}
property var audioPlayer: Audio {
id: audio
source: info.audioUrl
onStatusChanged: {
if (status === Audio.EndOfMedia) {
console.log("Song ended");
UserPlaylistModel.next();
}
}
}
contentHeight: height * 2
ColumnLayout {
id: footerLayout
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
// margin from top
Item {
Layout.minimumHeight: footerItem.height - footerItem.minimizedPlayerHeight
}
Controls.Control {
Layout.fillWidth: true
padding: 0
implicitHeight: footerItem.minimizedPlayerHeight
// minimized player background
background: Item {
Rectangle {
ColumnLayout {
anchors.fill: parent
color: Qt.rgba(25, 25, 30, 1)
}
Image {
opacity: 0.2
source: info.thumbnail
asynchronous: true
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
}
layer.enabled: true
layer.effect: HueSaturation {
cached: true
lightness: -0.5
saturation: 1.9
Kirigami.Heading {
Layout.fillWidth: true
layer.enabled: true
layer.effect: FastBlur {
cached: true
radius: 64
transparentBorder: false
level: 2
text: title
}
Controls.Label {
Layout.fillWidth: true
text: artists
}
}
}
MinimizedPlayerControls {
progressBarHeight: footerItem.progressBarHeight
minimizedPlayerContentHeight: footerItem.minimizedPlayerContentHeight
height: minimizedPlayerHeight
focus: true
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
info: footerItem.videoInfoExtractor
audio: footerItem.audioPlayer
onRequestOpen: toOpen.start();
Controls.BusyIndicator {
anchors.centerIn: parent
visible: playlistModel.loading || playlistModel.loading
}
}
MaximizedPlayerPage {
id: maximizedPlayerPage
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: footerItem.height
info: footerItem.videoInfoExtractor
audio: footerItem.audioPlayer
onRequestClose: toClose.start();
}
//GridLayout {
//id: playerLayout
//readonly property bool mobile: width > height
//Layout.fillHeight: footerItem.maximized
//Layout.fillWidth: true
//flow: width > height ? GridLayout.LeftToRight : GridLayout.TopToBottom
//visible: footerItem.maximized
//ColumnLayout {
//Layout.preferredWidth: footerLayout.width * (playerLayout.mobile ? 1 : 0.5)
//Layout.preferredHeight: footerLayout.height * (playerLayout.mobile ? 0.5 : 1)
//Item {
//Layout.fillWidth: true
//Layout.fillHeight: true
//Image {
//anchors.fill: parent
//source: info.thumbnail
//fillMode: Image.PreserveAspectFit
//}
//}
//Controls.RoundButton {
//readonly property QtObject favouriteWatcher: Library.favouriteWatcher(info.videoId)
//Layout.alignment: Qt.AlignHCenter
//icon.name: favouriteWatcher ? (favouriteWatcher.isFavourite ? "starred-symbolic" : "non-starred-symbolic") : ""
//onClicked: if (favouriteWatcher)
//favouriteWatcher.isFavourite
//? Library.removeFavourite(info.videoId)
//: Library.addFavourite(info.videoId, info.title)
//}
//}
//Item {
//Layout.fillWidth: true
//Layout.fillHeight: true
//Layout.preferredWidth: footerLayout.width * (playerLayout.mobile ? 1 : 0.5)
//Layout.preferredHeight: footerLayout.height * (playerLayout.mobile ? 0.5 : 1)
//Controls.ScrollView {
//anchors.fill: parent
//ListView {
//anchors.fill: parent
//id: playlistView
//clip: true
//Controls.BusyIndicator {
//anchors.centerIn: parent
//visible: UserPlaylistModel.loading || UserPlaylistModel.loading
//}
//onCountChanged: {
//if (count < 1) {
//footerItem.maximized = false
//}
//}
//model: UserPlaylistModel
//delegate: Kirigami.SwipeListItem {
//id: delegateItem
//required property string title
//required property string videoId
//required property string artists
//required property bool isCurrent
//highlighted: isCurrent
//onClicked: UserPlaylistModel.skipTo(videoId)
//ColumnLayout {
//Layout.fillWidth: true
//Kirigami.Heading {
//elide: Text.ElideRight
//Layout.fillWidth: true
//level: 2
//text: title
//}
//Controls.Label {
//elide: Text.ElideRight
//Layout.fillWidth: true
//text: artists
//}
//}
//actions: [
//Kirigami.Action {
//text: i18n("Remove Track")
//icon.name: "list-remove"
//onTriggered: UserPlaylistModel.remove(delegateItem.videoId)
//}
//]
//}
//header: Controls.ToolBar {
//anchors.left: parent.left
//anchors.right: parent.right
//RowLayout {
//anchors.fill: parent
//Item {
//Layout.fillWidth: true
//}
//Controls.ToolButton {
//text: i18n("Clear")
//icon.name: "edit-clear-all"
//onClicked: UserPlaylistModel.clear()
//}
//Controls.ToolButton {
//text: i18n("Shuffle")
//icon.name: "media-playlist-shuffle"
//onClicked: UserPlaylistModel.shufflePlaylist()
//}
//}
//}
//}
//}
//}
//}
//Kirigami.Separator {
//Layout.fillWidth: true
//}
//RowLayout {
//id: player
//Layout.fillWidth: true
//Layout.fillHeight: !footer.maximized
//Controls.ToolButton {
//display: Controls.AbstractButton.IconOnly
//Layout.preferredWidth: parent.height
//Layout.fillHeight: true
//enabled: info.audioUrl != ""
//text: audio.playbackState === Audio.PlayingState ? i18n("Pause") : i18n("Play")
//visible: !info.loading
//icon.name: audio.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
//onClicked: audio.playbackState === Audio.PlayingState ? audio.pause() : audio.play()
//}
//Controls.ToolButton {
//display: Controls.AbstractButton.IconOnly
//Layout.preferredWidth: parent.height
//Layout.fillHeight: true
//enabled: UserPlaylistModel.canSkip
//visible: !info.loading
//icon.name: "media-skip-forward"
//onClicked: UserPlaylistModel.next()
//}
//Controls.BusyIndicator {
//Layout.fillHeight: true
//visible: info.loading
//}
//ColumnLayout {
//Layout.fillHeight: true
//Layout.topMargin: Kirigami.Units.smallSpacing
//Layout.bottomMargin: Kirigami.Units.smallSpacing
//Kirigami.Heading {
//level: 3
//Layout.fillWidth: true
//elide: Qt.ElideRight
//text: info.title ? info.title : i18n("No media playing")
//}
//RowLayout{
//Controls.Label{
//visible: info.title
//text: PlayerUtils.formatTimestamp(audio.position)
//}
//Controls.Slider {
//Layout.fillWidth: true
//from: 0
//to: audio.duration
//value: audio.position
//enabled: audio.seekable
//onMoved: {
//console.log("Value:", value);
//audio.seek(Math.floor(value));
//}
//Behavior on value {
//NumberAnimation {
//duration: 1000
//}
//}
//}
//Controls.Label{
//visible: info.title
//text: PlayerUtils.formatTimestamp(audio.duration)
//}
//}
//}
//Controls.ToolButton {
//display: Controls.AbstractButton.IconOnly
//Layout.preferredWidth: parent.height
//text: i18n("Expand")
//enabled: playlistView.count > 0
//Layout.fillHeight: true
//icon.name: footerItem.maximized ? "arrow-down" : "arrow-up"
//onClicked: footerItem.maximized = !footerItem.maximized
//}
//}
}
//Behavior on height {
//NumberAnimation {
//}
//}
Behavior on height {
NumberAnimation {
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
RowLayout {
id: player
Layout.fillWidth: true
Layout.fillHeight: !footer.maximized
VideoInfoExtractor {
id: info
onSongChanged: audio.play()
videoId: playlistModel.currentVideoId
}
Audio {
id: audio
source: info.audioUrl
onStatusChanged: {
if (status === Audio.EndOfMedia) {
console.log("Song ended")
playlistModel.next()
}
}
}
Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
Layout.preferredWidth: parent.height
Layout.fillHeight: true
enabled: info.audioUrl != ""
text: audio.playbackState === Audio.PlayingState ? i18n("Pause") : i18n("Play")
visible: !info.loading
icon.name: audio.playbackState === Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
onClicked: audio.playbackState === Audio.PlayingState ? audio.pause() : audio.play()
}
Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
Layout.preferredWidth: parent.height
Layout.fillHeight: true
enabled: playlistView.count > 1
visible: !info.loading
icon.name: "media-skip-forward"
onClicked: playlistModel.next()
}
Controls.BusyIndicator {
Layout.fillHeight: true
visible: info.loading
}
ColumnLayout {
Layout.fillHeight: true
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.bottomMargin: Kirigami.Units.largeSpacing
Kirigami.Heading {
level: 2
text: info.title ? info.title : i18n("No media playing")
}
Controls.Slider {
Layout.fillWidth: true
from: 0
to: audio.duration
value: audio.position
enabled: audio.seekable
onMoved: {
console.log("Value:", value)
audio.seek(Math.floor(value))
}
Behavior on value {
NumberAnimation {
}
}
}
}
Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
Layout.preferredWidth: parent.height
text: i18n("Expand")
Layout.fillHeight: true
icon.name: footer.maximized ? "arrow-down" : "arrow-up"
onClicked: footer.maximized = !footer.maximized
}
}
}
-102
View File
@@ -1,102 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12 as Controls
import org.kde.kirigami 2.12 as Kirigami
import org.kde.ytmusic 1.0
Kirigami.ScrollablePage {
property alias playlistId: playlistModel.playlistId
title: playlistModel.title
SongMenu {
id: menu
}
ListView {
id: songList
header: ListHeader {
visibleActions: [
Kirigami.Action {
icon.name: "media-playback-start"
text: i18n("Play")
onTriggered: {
applicationWindow().playPlaylist(playlistModel.playlistId)
}
},
Kirigami.Action {
icon.name: "media-playlist-shuffle"
text: i18n("Shuffle")
onTriggered: {
applicationWindow().playShufflePlaylist(playlistModel.playlistId)
}
}
]
title: playlistModel.title
imageSourceURL: playlistModel.thumbnailUrl
width: songList.width
subtitle: i18n("Playlist")
}
model: PlaylistModel {
id: playlistModel
}
reuseItems: true
delegate: Kirigami.SwipeListItem {
id: delegateItem
required property string title
required property string videoId
required property var artists
required property string thumbnailUrl
required property string artistsDisplayString
RowLayout {
Layout.fillHeight: true
RoundedImage {
source: delegateItem.thumbnailUrl
height: 35
width: height
radius: 5
}
ColumnLayout {
Controls.Label {
Layout.fillWidth: true
text: title
elide: Qt.ElideRight
}
Controls.Label {
Layout.fillWidth: true
visible: delegateItem.artistsDisplayString
color: Kirigami.Theme.disabledTextColor
text: delegateItem.artistsDisplayString
elide: Qt.ElideRight
}
}
}
actions: [
Kirigami.Action {
icon.name: "view-more-horizontal-symbolic"
text: i18n("More")
onTriggered: menu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, delegateItem.artistsDisplayString)
}
]
onClicked: play(videoId)
}
Controls.BusyIndicator {
anchors.centerIn: parent
visible: playlistModel.loading
}
}
}
-32
View File
@@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: 2022 Mathis Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
Rectangle {
property alias radius: mask.radius
property alias source: image.source
id: icon
color: "transparent"
Rectangle {
id: imageSource
anchors.fill: parent
Image {
id:image
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
asynchronous: true
}
visible: false
layer.enabled: true
}
RoundedMask {
id: mask
anchors.fill: parent
colorSource: imageSource
}
}
-26
View File
@@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: Mathis Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
Rectangle {
id: mask
radius: 10
layer.enabled: true
layer.samplerName: "maskSource"
property var colorSource
layer.effect: ShaderEffect {
property var colorSource: mask.colorSource
fragmentShader: "
uniform lowp sampler2D colorSource;
uniform lowp sampler2D maskSource;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor = texture2D(colorSource, qt_TexCoord0) * texture2D(maskSource, qt_TexCoord0).a * qt_Opacity;
}"
}
}
-158
View File
@@ -1,158 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.14 as Controls
import org.kde.kirigami 2.14 as Kirigami
import org.kde.ytmusic 1.0
Kirigami.ScrollablePage {
property alias searchQuery: searchModel.searchQuery
title: searchQuery
ListView {
reuseItems: true
section.delegate: Kirigami.ListSectionHeader {
required property string section
text: {
switch (parseInt(section)) {
case SearchModel.Album:
return i18n("Albums")
case SearchModel.Artist:
return i18n("Artists")
case SearchModel.Playlist:
return i18n("Playlists")
case SearchModel.Song:
return i18n("Songs")
case SearchModel.Video:
return i18n("Videos")
}
return i18n("Unknown")
}
}
section.property: "type"
model: SearchModel {
id: searchModel
onOpenAlbum: (browseId) => {
pageStack.push("qrc:/AlbumPage.qml", {
"browseId": browseId
})
}
onOpenArtist: (channelId, radioId, shuffleId) => {
pageStack.push("qrc:/ArtistPage.qml", {
"channelId": channelId,
"radioId": radioId,
"shuffleId": shuffleId
})
}
onOpenPlaylist: (browseId) => {
pageStack.push("qrc:/PlaylistPage.qml", {
"playlistId": browseId
})
}
onOpenSong: (videoId) => {
play(videoId)
}
onOpenVideo: (videoId, title) => {
videoPlayMessage.text = i18n("Video playback is not supported yet. Do you want to play only the audio of \"%1\"?", title)
videoPlayMessage.visible = true
videoPlayMessage.okAction = function() {
play(videoId)
videoPlayMessage.visible = false
}
videoPlayMessage.cancelAction = function() {
videoPlayMessage.visible = false
}
}
}
SongMenu {
id: menu
}
delegate: Kirigami.SwipeListItem {
id: delegateItem
required property int index
required property string title
required property int type
required property string videoId
required property var artists
required property string artistsDisplayString
required property string radioPlaylistId
required property string thumbnailUrl
RowLayout {
Layout.fillHeight: true
RoundedImage {
source: delegateItem.thumbnailUrl
height: 35
width: height
radius: delegateItem.type === SearchModel.Artist?height/2:5
}
ColumnLayout {
Controls.Label {
Layout.fillWidth: true
text: title
elide: Qt.ElideRight
}
Controls.Label {
Layout.fillWidth: true
visible: delegateItem.artistsDisplayString
color: Kirigami.Theme.disabledTextColor
text: delegateItem.artistsDisplayString
elide: Qt.ElideRight
}
}
}
actions: [
Kirigami.Action {
icon.name: "view-more-horizontal-symbolic"
text: i18n("More")
visible: delegateItem.type === SearchModel.Song || delegateItem.type === SearchModel.Video
onTriggered: menu.openForSong(delegateItem.videoId, delegateItem.title, delegateItem.artists, delegateItem.artistsDisplayString)
}
]
onClicked: searchModel.triggerItem(index)
}
Controls.BusyIndicator {
anchors.centerIn: parent
visible: searchModel.loading
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
width: parent.width - (Kirigami.Units.largeSpacing * 4)
visible: parent.count === 0 && !searchModel.loading
text: i18n("Find music on YouTube Music")
helpfulAction: Kirigami.Action {
onTriggered: searchField.forceActiveFocus()
text: i18n("Search")
icon.name: "search"
}
}
}
footer: ConfirmationMessage {
id: videoPlayMessage
}
}
-51
View File
@@ -1,51 +0,0 @@
// SPDX-FileCopyrightText: 2022 Mathis Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import org.kde.ytmusic 1.0
Controls.Menu{
id: menu
property string videoId
property string songTitle
property var artists
property string artistsDisplayString
function openForSong(videoId, songTitle, artists, artistsDisplayString) {
menu.videoId = videoId
menu.songTitle = songTitle
menu.artists = artists
menu.artistsDisplayString = artistsDisplayString
menu.popup()
}
Controls.MenuItem {
text: i18n("Play Next")
icon.name: "go-next"
onTriggered: UserPlaylistModel.playNext(menu.videoId, menu.SongTitle, artists)
}
Controls.MenuItem {
text: i18n("Add to queue")
icon.name: "media-playlist-append"
onTriggered: UserPlaylistModel.append(menu.videoId, menu.SongTitle, artists)
}
Controls.MenuItem {
readonly property QtObject favouriteWatcher: Library.favouriteWatcher(menu.videoId)
text: favouriteWatcher ? (favouriteWatcher.isFavourite ? i18n("Remove Favourite"): i18n("Add Favourite")): ""
icon.name: favouriteWatcher ? (favouriteWatcher.isFavourite ? "starred-symbolic" : "non-starred-symbolic") : ""
onTriggered: {
if (favouriteWatcher) {
if (favouriteWatcher.isFavourite) {
Library.removeFavourite(menu.videoId)
} else {
Library.addFavourite(videoId, songTitle, artistsDisplayString, "")
}
}
}
}
}
+74 -309
View File
@@ -1,331 +1,96 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.1
import QtQuick.Controls 2.12 as Controls
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.14 as Kirigami
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.3
import org.kde.ytmusic 1.0
Kirigami.ApplicationWindow {
id: root
property alias searchField: searchLoader.item // TODO
property bool wideScreen: width >= 600
property bool showSearch: false // only applicable if not widescreen
// so that there is still a separator, since the header style is none
Kirigami.Separator {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
visible: pageStack.currentItem.objectName == "libraryPage"
&& pageStack.layers.depth == 1
}
header: Controls.Control {
padding: Kirigami.Units.largeSpacing
background: Rectangle {
width: parent.width
height: parent.height
anchors.fill: parent
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Header
color: Kirigami.Theme.backgroundColor
}
contentItem: RowLayout {
Controls.ToolButton {
id: searchBack
Layout.alignment: Qt.AlignLeft
visible:
(!root.wideScreen && root.showSearch && root.searchField.text === "")
|| pageStack.layers.depth > 1
text: i18n("Back")
icon.name: "go-previous-view"
display: Controls.ToolButton.IconOnly
onClicked: if (root.showSearch)
root.showSearch = false
else
pageStack.layers.pop()
}
Kirigami.Heading {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: Kirigami.Units.largeSpacing
level: 1
text: "AudioTube"
visible: !root.wideScreen && !root.showSearch
}
// spacer
Item {
Layout.fillWidth: !root.wideScreen
}
Loader {
id: searchLoader
visible: root.wideScreen || root.showSearch
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.maximumWidth: 400
sourceComponent: searchFieldComponent
}
Controls.ToolButton {
Layout.alignment: Qt.AlignRight
visible: !root.wideScreen && !root.showSearch
text: i18n("Search")
icon.name: "search"
display: Controls.ToolButton.TextBesideIcon
onClicked: {
root.showSearch = true
root.searchField.forceActiveFocus();
root.searchField.popup.open();
}
}
}
}
title: i18n("AudioTube")
contextDrawer: Kirigami.ContextDrawer {
id: contextDrawer
}
pageStack.initialPage: "qrc:/LibraryPage.qml"
pageStack.initialPage: Kirigami.ScrollablePage {
title: i18n("Youtube Music")
header: Controls.Control {
padding: Kirigami.Units.largeSpacing
contentItem: Kirigami.SearchField {
onAccepted: {
searchModel.searchQuery = text
}
}
}
ListView {
model: SearchModel {
id: searchModel
onOpenAlbum: (browseId) => {
pageStack.push("qrc:/AlbumPage.qml", {
"browseId": browseId
})
}
onOpenArtist: (channelId) => {
pageStack.push("qrc:/ArtistPage.qml", {
"channelId": channelId
})
}
onOpenPlaylist: (browseId) => {
}
onOpenSong: (videoId) => {
play(videoId)
}
onOpenVideo: (videoId) => {
}
}
delegate: Kirigami.BasicListItem {
required property int index
required property string title
required property int type
text: title
icon: {
switch (type) {
case SearchModel.Artist:
return "view-media-artist"
case SearchModel.Album:
return "media-album-cover"
case SearchModel.Playlist:
return "view-media-playlist"
case SearchModel.Song:
return "emblem-music-symbolic"
case SearchModel.Video:
return "emblem-videos-symbolic"
}
}
onClicked: searchModel.triggerItem(index)
}
Controls.BusyIndicator {
anchors.centerIn: parent
visible: searchModel.loading
}
}
}
function play(videoId) {
UserPlaylistModel.initialVideoId = videoId
footer.videoId = videoId
}
function playPlaylist(playlistId) {
UserPlaylistModel.playlistId = playlistId
footer.playlistId = playlistId
}
function playShufflePlaylist(playlistId) {
UserPlaylistModel.shuffle = true
UserPlaylistModel.playlistId = playlistId
}
Connections {
target: ErrorHandler
function onErrorOccurred(error) {
showPassiveNotification(error)
}
}
Component {
id: searchAlbum
ColumnLayout {
Layout.alignment: Qt.AlignTop
id: mpdelegateItem
width: 100
Layout.maximumWidth: 70
Kirigami.ShadowedRectangle {
id: recCover
MouseArea {
id: recArea
anchors.fill: parent
onClicked: play(model.videoId)
hoverEnabled: !Kirigami.Settings.hasTransientTouchInput
onEntered: {
if (!Kirigami.Settings.hasTransientTouchInput)
recSelected.visible = true
}
onExited: recSelected.visible = false
}
Layout.margins: 5
width: 70
height: 70
radius: 10
shadow.size: 15
shadow.xOffset: 5
shadow.yOffset: 5
shadow.color: Qt.rgba(0, 0, 0, 0.2)
Rectangle {
width: 70
height: 70
color: "transparent"
ThumbnailSource {
id: thumbnailSource
videoId: model ? model.videoId : ""
}
RoundedImage {
source: thumbnailSource.cachedPath
height: parent.height
width: height
radius: 10
}
Rectangle {
id: recSelected
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.hoverColor
radius: 10
opacity: 0.2
}
visible: false
anchors.fill: parent
radius: 9
border.color: Kirigami.Theme.hoverColor
border.width: 2
color: "transparent"
}
}
}
Controls.Label {
leftPadding:5
Layout.maximumWidth: 70
text: model ? model.title : ""
elide: Qt.ElideRight
wrapMode: Text.WordWrap
Layout.maximumHeight: 40
}
Item {
height: 5
}
}
}
Component {
id: searchFieldComponent
Kirigami.SearchField {
id: searchField
autoAccept: false
selectByMouse: true
onPressed: {
popup.open()
}
property alias popup: popup
property var filterExpression: new RegExp(`.*${searchField.text}.*`, "i")
Controls.Popup {
padding: 1
id: popup
x: searchField.y
y: searchField.y + searchField.height
width: searchField.width
height: completionList
? Math.min(completionList.count * Kirigami.Units.gridUnit * 2 + Kirigami.Units.gridUnit * 2 + recents.implicitHeight, Kirigami.Units.gridUnit * 20)
: Kirigami.Units.gridUnit * 20
contentItem: Controls.ScrollView {
ColumnLayout {
width: popup.width
Controls.ScrollView {
id: recents
Layout.fillWidth: true
Layout.maximumWidth: popup.width - 23
visible: searchField.text && recentsRepeater.count > 0
leftPadding: 10
topPadding: 10
Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff
RowLayout {
spacing: 10
Layout.fillWidth: true
Layout.maximumWidth: popup.width - 23
Repeater {
id: recentsRepeater
Layout.fillWidth: true
model: SortFilterModel {
filterRole: PlaybackHistoryModel.Title
filterRegularExpression: searchField.filterExpression
sourceModel: Library.playbackHistory
}
delegate: Kirigami.DelegateRecycler {
sourceComponent: searchAlbum
}
}
}
}
Kirigami.Separator{
Layout.fillWidth: true
}
Repeater {
Layout.fillWidth: true
id: completionList
model: SortFilterModel {
sourceModel: Library.searches
filterRegularExpression: searchField.filterExpression
}
delegate: Controls.ItemDelegate {
Kirigami.Theme.colorSet: Kirigami.Theme.Window
Kirigami.Theme.inherit: false
Layout.fillWidth: true
id: completionDelegate
width: parent.width
height: Kirigami.Units.gridUnit * 2
text: model.display
onClicked: {
searchField.text = model.display
searchField.accepted()
}
}
}
}
}
}
onAccepted: {
while (pageStack.depth > 0) {
pageStack.pop()
}
pageStack.layers.clear()
if (text) {
Library.addSearch(text)
pageStack.push("qrc:/SearchPage.qml", {
"searchQuery": text})
} else {
pageStack.push("qrc:/LibraryPage.qml")
}
popup.close()
}
}
}
pageStack.anchors.bottomMargin: playerFooter.minimizedPlayerHeight + 1
// media player
PlayerFooter {
id: playerFooter
anchors.topMargin: -root.header.height
anchors.fill: parent
// only expand flicking area to full screen when it is open
z: (contentY === 0) ? -1 : 999
}
footer: PlayerFooter {}
}
-11
View File
@@ -1,11 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "errorhandler.h"
ErrorHandler::ErrorHandler(QObject *parent)
: QObject(parent)
{
connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, &ErrorHandler::errorOccurred);
}
-19
View File
@@ -1,19 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include "asyncytmusic.h"
class ErrorHandler : public QObject
{
Q_OBJECT
public:
explicit ErrorHandler(QObject *parent = nullptr);
Q_SIGNAL void errorOccurred(const QString &error);
};
-4
View File
@@ -1,6 +1,2 @@
# SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
#
# SPDX-License-Identifier: BSD-2-Clause
add_executable(ytm_example main.cpp)
target_link_libraries(ytm_example PRIVATE ytm)
+4 -9
View File
@@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include <ytmusic.h>
@@ -8,7 +8,7 @@
int main() {
const auto ytm = YTMusic();
const auto results = ytm.search("Another one bites the dust");
const auto results = ytm.search("Syd Matters");
std::cout << "Found " << results.size() << " results." << std::endl;
@@ -17,12 +17,7 @@ int main() {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, search::Album>) {
std::cout << arg.title << std::endl;
const album::Album album = ytm.get_album(arg.browse_id);
for (const auto &track : album.tracks) {
if (track.video_id.has_value()) {
ytm.extract_video_info(*track.video_id);
}
}
ytm.get_album(arg.browse_id);
} else if constexpr (std::is_same_v<T, search::Artist>) {
std::cout << arg.artist << std::endl;
const artist::Artist artist = ytm.get_artist(arg.browse_id);
@@ -42,7 +37,7 @@ int main() {
} else if constexpr (std::is_same_v<T, search::Video>) {
std::cout << arg.title << std::endl;
} else {
std::cout << "non exaustive visitor";
std::cout << "non excaustive visitor";
}
}, result);
}
-268
View File
@@ -1,268 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "library.h"
#include <QStandardPaths>
#include <QDir>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QStringBuilder>
#include "asyncdatabase.h"
namespace ranges = std::ranges;
Library::Library(QObject *parent)
: QObject{parent}
, m_database(ThreadedDatabase::establishConnection([]() -> DatabaseConfiguration {
const auto databaseDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// Make sure the database directory exists
QDir(databaseDirectory).mkpath(QStringLiteral("."));
DatabaseConfiguration config;
config.setDatabaseName(databaseDirectory % QDir::separator() % "library.sqlite");
config.setType(DATABASE_TYPE_SQLITE);
return config;
}()))
{
m_database->runMigrations(":/migrations/");
}
Library::~Library() = default;
Library &Library::instance()
{
static Library inst;
return inst;
}
FavouritesModel *Library::favourites()
{
auto future = m_database->getResults<Song>("select * from favourites natural join songs order by favourites.rowid desc");
return new FavouritesModel(std::move(future), this);
}
void Library::addFavourite(const QString &videoId, const QString &title, const QString &artist, const QString &album)
{
connectFuture(addSong(videoId, title, artist, album), this, [=, this] {
connectFuture(m_database->execute("insert or ignore into favourites (video_id) values (?)", videoId), this, &Library::favouritesChanged);
});
}
void Library::removeFavourite(const QString &videoId)
{
connectFuture(m_database->execute("delete from favourites where video_id = ?", videoId), this, &Library::favouritesChanged);
}
FavouriteWatcher *Library::favouriteWatcher(const QString &videoId)
{
if (videoId.isEmpty()) {
return nullptr;
}
return new FavouriteWatcher(this, videoId);
}
SearchHistoryModel *Library::searches()
{
return new SearchHistoryModel(m_database->getResults<SingleValue<QString>>("select distinct (search_query) from searches order by search_id desc"), this);
}
void Library::addSearch(const QString &text)
{
connectFuture(m_database->execute("insert into searches (search_query) values (?)", text), this, &Library::searchesChanged);
}
PlaybackHistoryModel *Library::playbackHistory()
{
auto future = m_database->getResults<PlayedSong>("select * from played_songs natural join songs");
return new PlaybackHistoryModel(std::move(future), this);
}
void Library::addPlaybackHistoryItem(const QString &videoId, const QString &title, const QString &artist, const QString &album)
{
connectFuture(addSong(videoId, title, artist, album), this, [=, this] {
connectFuture(m_database->execute("insert or ignore into played_songs (video_id, plays) values (?, ?)", videoId, 0), this, [=, this] {
connectFuture(m_database->execute("update played_songs set plays = plays + 1 where video_id = ? ", videoId), this, &Library::playbackHistoryChanged);
});
});
}
PlaybackHistoryModel *Library::mostPlayed()
{
auto future = m_database->getResults<PlayedSong>("select * from played_songs natural join songs order by plays desc limit 10");
return new PlaybackHistoryModel(std::move(future), this);
}
QNetworkAccessManager &Library::nam()
{
return m_networkImageCacher;
}
QFuture<void> Library::addSong(const QString &videoId, const QString &title, const QString &artist, const QString &album)
{
// replace is used here to update songs from times when we didn't store artist and album
return m_database->execute("insert or replace into songs (video_id, title, artist, album) values (?, ?, ?, ?)", videoId, title, artist, album);
}
void ThumbnailSource::setVideoId(const QString &id) {
if (m_videoId == id) {
return;
}
m_videoId = id;
Q_EMIT videoIdChanged();
const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) % QDir::separator() % "thumbnails";
QDir(cacheDir).mkpath(QStringLiteral("."));
const QString cacheLocation = cacheDir % QDir::separator() % id % ".webp";
if (QFile::exists(cacheLocation)) {
setCachedPath(QUrl::fromLocalFile(cacheLocation));
return;
}
auto *reply = Library::instance().nam().get(QNetworkRequest(QUrl("https://i.ytimg.com/vi_webp/" % m_videoId % "/maxresdefault.webp")));
auto storeResult = [this, cacheLocation](QNetworkReply *reply) {
QFile file(cacheLocation);
file.open(QFile::WriteOnly);
file.write(reply->readAll());
setCachedPath(QUrl::fromLocalFile(cacheLocation));
reply->deleteLater();
};
connect(reply, &QNetworkReply::errorOccurred, this, [this, storeResult](auto error) {
if (error == QNetworkReply::NetworkError::ContentNotFoundError) {
qDebug() << "Naive thumbnail resolution failed, falling back to yt-dlp (slower)";
connectFuture(YTMusicThread::instance()->extractVideoInfo(m_videoId), this, [this, storeResult](auto info) {
auto *reply = Library::instance().nam().get(QNetworkRequest(QUrl(QString::fromStdString(info.thumbnail))));
connect(reply, &QNetworkReply::finished, this, [reply, storeResult]() {
storeResult(reply);
});
});
}
});
connect(reply, &QNetworkReply::finished, this, [reply, storeResult]() {
storeResult(reply);
});
}
PlaybackHistoryModel::PlaybackHistoryModel(QFuture<std::vector<PlayedSong> > &&songs, QObject *parent)
: QAbstractListModel(parent)
{
connectFuture(songs, this, [this](const auto songs) {
beginResetModel();
m_playedSongs = songs;
endResetModel();
});
}
QHash<int, QByteArray> PlaybackHistoryModel::roleNames() const {
return {
{Roles::VideoId, "videoId"},
{Roles::Title, "title"},
{Roles::Artist, "artists"},
{Roles::Plays, "plays"}
};
}
int PlaybackHistoryModel::rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : m_playedSongs.size();
}
QVariant PlaybackHistoryModel::data(const QModelIndex &index, int role) const {
switch (role) {
case Roles::VideoId:
return m_playedSongs.at(index.row()).videoId;
case Roles::Title:
return m_playedSongs.at(index.row()).title;
case Roles::Artist:
return m_playedSongs.at(index.row()).artist;
case Roles::Plays:
return m_playedSongs.at(index.row()).plays;
}
Q_UNREACHABLE();
}
FavouritesModel::FavouritesModel(QFuture<std::vector<Song>> &&songs, QObject *parent)
: QAbstractListModel(parent)
{
connectFuture(songs, this, [this](const auto songs) {
beginResetModel();
m_favouriteSongs = songs;
endResetModel();
});
}
QHash<int, QByteArray> FavouritesModel::roleNames() const {
return {
{Roles::VideoId, "videoId"},
{Roles::Title, "title"},
{Roles::Artist, "artists"}
};
}
int FavouritesModel::rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : m_favouriteSongs.size();
}
QVariant FavouritesModel::data(const QModelIndex &index, int role) const {
switch (role) {
case Roles::VideoId:
return m_favouriteSongs.at(index.row()).videoId;
case Roles::Title:
return m_favouriteSongs.at(index.row()).title;
case Roles::Artist:
return m_favouriteSongs.at(index.row()).artist;
}
Q_UNREACHABLE();
}
FavouriteWatcher::FavouriteWatcher(Library *library, const QString &videoId)
: QObject(library), m_videoId(videoId), m_library(library)
{
auto update = [this] {
connectFuture(m_library->database().getResult<SingleValue<bool>>("select count(*) > 0 from favourites where video_id = ?", m_videoId), this, [this](auto count) {
if (count) {
m_isFavourite = count->value;
Q_EMIT isFavouriteChanged();
}
});
};
update();
connect(library, &Library::favouritesChanged, this, update);
}
bool FavouriteWatcher::isFavourite() const {
return m_isFavourite;
}
SearchHistoryModel::SearchHistoryModel(QFuture<std::vector<SingleValue<QString>>> &&historyFuture, QObject *parent)
: QAbstractListModel(parent)
{
connectFuture(historyFuture, this, [this](const auto history) {
beginResetModel();
m_history = history;
endResetModel();
});
}
int SearchHistoryModel::rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : m_history.size();
}
QVariant SearchHistoryModel::data(const QModelIndex &index, int role) const {
switch (role) {
case Qt::DisplayRole:
return m_history[index.row()].value;
}
Q_UNREACHABLE();
}
-189
View File
@@ -1,189 +0,0 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQuickAsyncImageProvider>
#include <QNetworkAccessManager>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <memory>
#include "asyncytmusic.h"
#include "asyncdatabase.h"
class FavouriteWatcher;
struct Song {
using ColumnTypes = std::tuple<QString, QString, QString, QString>;
static Song fromSql(ColumnTypes tuple) {
auto [videoId, title, artist, album] = tuple;
return Song {videoId, title, artist, album};
}
QString videoId;
QString title;
QString artist;
QString album;
};
class FavouritesModel : public QAbstractListModel {
Q_OBJECT
enum Roles {
VideoId = Qt::UserRole + 1,
Title,
Artist,
};
public:
FavouritesModel(QFuture<std::vector<Song>> &&songs, QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
std::vector<Song> m_favouriteSongs;
};
struct PlayedSong {
using ColumnTypes = std::tuple<QString, int, QString, QString, QString>;
static PlayedSong fromSql(ColumnTypes tuple) {
auto [videoId, plays, title, artist, album] = tuple;
return PlayedSong {videoId, title, artist, album, plays};
}
QString videoId;
QString title;
QString artist;
QString album;
int plays;
};
class PlaybackHistoryModel : public QAbstractListModel {
Q_OBJECT
public:
enum Roles {
VideoId = Qt::UserRole + 1,
Title,
Artist,
Plays
};
Q_ENUM(Roles);
PlaybackHistoryModel(QFuture<std::vector<PlayedSong>> &&songs, QObject *parent = nullptr);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
std::vector<PlayedSong> m_playedSongs;
};
class SearchHistoryModel : public QAbstractListModel {
Q_OBJECT
public:
SearchHistoryModel(QFuture<std::vector<SingleValue<QString>>> &&historyFuture, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
std::vector<SingleValue<QString>> m_history;
};
class Library : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractListModel *favourites READ favourites NOTIFY favouritesChanged)
Q_PROPERTY(QAbstractListModel *searches READ searches NOTIFY searchesChanged)
Q_PROPERTY(QAbstractListModel *playbackHistory READ playbackHistory NOTIFY playbackHistoryChanged)
Q_PROPERTY(QAbstractListModel *mostPlayed READ mostPlayed NOTIFY playbackHistoryChanged)
public:
explicit Library(QObject *parent = nullptr);
~Library();
static Library &instance();
FavouritesModel *favourites();
Q_SIGNAL void favouritesChanged();
Q_INVOKABLE void addFavourite(const QString &videoId, const QString &title, const QString &artist, const QString &album);
Q_INVOKABLE void removeFavourite(const QString &videoId);
Q_INVOKABLE FavouriteWatcher *favouriteWatcher(const QString &videoId);
SearchHistoryModel *searches();
Q_SIGNAL void searchesChanged();
Q_INVOKABLE void addSearch(const QString &text);
PlaybackHistoryModel *playbackHistory();
Q_SIGNAL void playbackHistoryChanged();
Q_INVOKABLE void addPlaybackHistoryItem(const QString &videoId, const QString &title, const QString &artist, const QString &album);
PlaybackHistoryModel *mostPlayed();
QNetworkAccessManager &nam();
ThreadedDatabase &database() {
return *m_database;
}
private:
QFuture<void> addSong(const QString &videoId, const QString &title, const QString &artist, const QString &album);
QNetworkAccessManager m_networkImageCacher;
std::unique_ptr<ThreadedDatabase> m_database;
};
class ThumbnailSource : public QObject {
Q_OBJECT
Q_PROPERTY(QString videoId READ videoId WRITE setVideoId NOTIFY videoIdChanged)
Q_PROPERTY(QUrl cachedPath READ cachedPath NOTIFY cachedPathChanged)
QString videoId() const {
return m_videoId;
}
void setVideoId(const QString &id);
Q_SIGNAL void videoIdChanged();
QUrl cachedPath() const {
return m_cachedPath;
}
void setCachedPath(const QUrl &path) {
m_cachedPath = path;
cachedPathChanged();
}
Q_SIGNAL void cachedPathChanged();
private:
QString m_videoId;
QUrl m_cachedPath;
};
class FavouriteWatcher : public QObject {
Q_OBJECT
Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY isFavouriteChanged)
public:
FavouriteWatcher(Library *library, const QString &videoId);
bool isFavourite() const;
Q_SIGNAL void isFavouriteChanged();
Q_SIGNAL void videoIdChanged();
private:
QString m_videoId;
Library *m_library;
bool m_isFavourite = false;
};
+7 -34
View File
@@ -1,28 +1,18 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QSortFilterProxyModel>
#include <QUrl>
#include <KLocalizedContext>
#include <KLocalizedString>
#include <KCrash>
#include <KAboutData>
#include "searchmodel.h"
#include "albummodel.h"
#include "videoinfoextractor.h"
#include "artistmodel.h"
#include "userplaylistmodel.h"
#include "errorhandler.h"
#include "playlistmodel.h"
#include "playerutils.h"
#include "library.h"
#include "asyncdatabase.h"
constexpr auto URI = "org.kde.ytmusic";
@@ -30,14 +20,9 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
QCoreApplication::setApplicationName(QStringLiteral("audiotube"));
KLocalizedString::setApplicationDomain("audiotube");
KAboutData::setApplicationData(KAboutData(QStringLiteral("audiotube"), QStringLiteral("AudioTube")));
KCrash::initialize();
QCoreApplication::setOrganizationName("KDE");
QCoreApplication::setOrganizationDomain("kde.org");
QCoreApplication::setApplicationName("audiotube");
QQmlApplicationEngine engine;
@@ -46,20 +31,6 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
qmlRegisterType<VideoInfoExtractor>(URI, 1, 0, "VideoInfoExtractor");
qmlRegisterType<ArtistModel>(URI, 1, 0, "ArtistModel");
qmlRegisterType<PlaylistModel>(URI, 1, 0, "PlaylistModel");
qmlRegisterUncreatableType<PlaybackHistoryModel>(URI, 1, 0, "PlaybackHistoryModel","");
qmlRegisterType<QSortFilterProxyModel>(URI, 1, 0, "SortFilterModel");
qmlRegisterSingletonType<UserPlaylistModel>(URI, 1, 0, "UserPlaylistModel", [](QQmlEngine *, QJSEngine *) {
return new UserPlaylistModel();
});
qmlRegisterSingletonType<ErrorHandler>(URI, 1, 0, "ErrorHandler", [](QQmlEngine *, QJSEngine *) {
return new ErrorHandler();
});
qmlRegisterSingletonType<PlayerUtils>(URI, 1, 0, "PlayerUtils", [](QQmlEngine *, QJSEngine *) {
return new PlayerUtils();
});
qmlRegisterSingletonInstance<Library>(URI, 1, 0, "Library", &Library::instance());
qmlRegisterType<ThumbnailSource>(URI, 1, 0, "ThumbnailSource");
qmlRegisterAnonymousType<FavouriteWatcher>(URI, 1);
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
@@ -68,5 +39,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
return -1;
}
return app.exec();
int ret = app.exec();
AsyncYTMusic::stopInstance();
return ret;
}
View File
Whitespace-only changes.
@@ -1,8 +0,0 @@
-- SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
--
-- SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
drop table songs;
drop table played_songs;
drop table favourites;
drop table searches;
@@ -1,26 +0,0 @@
-- SPDX-FileCopyrightText: 2022 Jonah Brüchert <[email protected]>
--
-- SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
create table songs (
video_id Text primary key not null,
title Text not null,
artist Text,
album Text
);
create table played_songs (
video_id Text primary key not null,
plays Int not null,
foreign key (video_id) references songs(video_id)
);
create table favourites (
video_id Text primary key not null,
foreign key (video_id) references songs(video_id)
);
create table searches (
search_id integer primary key autoincrement not null,
search_query Text not null
);
-73
View File
@@ -1,73 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <variant>
#include <tuple>
#include <span>
#include <QtGlobal>
template <typename Tuple, typename Func, std::size_t i>
inline constexpr void iterate_impl(Tuple &tup, Func fun)
{
if constexpr(i >= std::tuple_size_v<std::decay_t<decltype(tup)>>) {
return;
} else {
fun(std::get<i>(tup));
return iterate_impl<Tuple, Func, i + 1>(tup, fun);
}
}
template <typename Tuple, typename Func>
inline constexpr void iterate_tuple(Tuple &tup, Func fun)
{
iterate_impl<Tuple, Func, 0>(tup, fun);
}
/// MultiIterableView is a view over multiple contigous memory arrays, like std::vector or std::array.
/// The elements of the arrays do not need to be of the same type.
template <typename... Arguments>
class MultiIterableView {
public:
explicit MultiIterableView(std::span<Arguments>... lists)
: m_vectors(std::forward_as_tuple(lists...))
{
}
explicit MultiIterableView(std::vector<Arguments> &...lists)
: MultiIterableView(std::span{lists}...)
{
}
[[nodiscard]] constexpr size_t size() const {
size_t s = 0;
iterate_tuple(m_vectors, [&](auto &vec) {
s += vec.size();
});
return s;
}
[[nodiscard]] constexpr std::variant<Arguments...> operator[](size_t i) const {
size_t s = 0;
std::variant<Arguments...> out;
iterate_tuple(m_vectors, [&](auto &vec) {
if (i >= s && i < s + vec.size()) {
out = vec[i - s];
}
s += vec.size();
});
return out;
}
[[nodiscard]] constexpr bool empty() const {
bool empty = true;
iterate_tuple(m_vectors, [&](auto &elem) {
empty &= elem.empty();
});
return empty;
}
private:
std::tuple<std::span<Arguments>...> m_vectors;
};
-15
View File
@@ -1,15 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "playerutils.h"
PlayerUtils::PlayerUtils(QObject *parent) : QObject(parent)
{
}
QString PlayerUtils::formatTimestamp(quint64 stamp)
{
return m_format.formatDuration(stamp, KFormat::FoldHours);
}
-22
View File
@@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <KFormat>
class PlayerUtils : public QObject
{
Q_OBJECT
public:
explicit PlayerUtils(QObject *parent = nullptr);
Q_INVOKABLE QString formatTimestamp(quint64 stamp);
private:
KFormat m_format;
};
+135 -63
View File
@@ -1,48 +1,47 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include "playlistmodel.h"
#include <QUrl>
#include "asyncytmusic.h"
#include "playlistutils.h"
#include <asyncytmusic.h>
PlaylistModel::PlaylistModel(QObject *parent)
: AbstractYTMusicModel(parent)
: QAbstractListModel(parent)
{
connect(this, &PlaylistModel::playlistIdChanged, this, [=, this] {
setLoading(true);
auto future = YTMusicThread::instance()->fetchPlaylist(m_playlistId);
connectFuture(future, this, [=, this](const playlist::Playlist &playlist) {
setLoading(false);
beginResetModel();
m_playlist = playlist;
std::sort(m_playlist.thumbnails.begin(), m_playlist.thumbnails.end());
endResetModel();
connect(this, &PlaylistModel::initialVideoIdChanged, this, [=] {
if (m_initialVideoId.isEmpty()) {
return;
}
Q_EMIT titleChanged();
Q_EMIT thumbnailUrlChanged();
});
setLoading(true);
AsyncYTMusic::instance().fetchWatchPlaylist(m_initialVideoId);
});
connect(this, &PlaylistModel::playlistIdChanged, this, [=] {
if (m_playlistId.isEmpty()) {
return;
}
setLoading(true);
AsyncYTMusic::instance().fetchWatchPlaylist(std::nullopt, m_playlistId);
});
connect(&AsyncYTMusic::instance(), &AsyncYTMusic::fetchWatchPlaylistFinished, this, [=](const watch::Playlist &playlist) {
setLoading(false);
beginResetModel();
m_playlist = playlist;
if (!m_playlist.tracks.empty()) {
m_currentVideoId = QString::fromStdString(m_playlist.tracks.front().video_id);
}
endResetModel();
currentVideoIdChanged();
});
}
int PlaylistModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : int(m_playlist.tracks.size());
}
QHash<int, QByteArray> PlaylistModel::roleNames() const
{
return {
{Title, "title"},
{Artists, "artists"},
{VideoId, "videoId"},
{ThumbnailUrl, "thumbnailUrl"},
{ArtistsDisplayString, "artistsDisplayString"}
};
return parent.isValid() ? 0 : m_playlist.tracks.size();
}
QVariant PlaylistModel::data(const QModelIndex &index, int role) const
@@ -50,21 +49,18 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const
switch (role) {
case Title:
return QString::fromStdString(m_playlist.tracks[index.row()].title);
case Artists:
return QVariant::fromValue(m_playlist.tracks[index.row()].artists);
case VideoId:
if (m_playlist.tracks[index.row()].video_id) {
return QString::fromStdString(*m_playlist.tracks[index.row()].video_id);
}
return {};
case ThumbnailUrl:
if (!m_playlist.tracks[index.row()].thumbnails.empty()) {
return QString::fromStdString(m_playlist.tracks[index.row()].thumbnails.front().url);
} else {
return {};
}
case ArtistsDisplayString:
return PlaylistUtils::artistsToString(m_playlist.tracks[index.row()].artists);
return QString::fromStdString(m_playlist.tracks[index.row()].video_id);
case Artists: {
const auto artists = m_playlist.tracks[index.row()].artists;
QStringList artistNames;
std::transform(artists.begin(), artists.end(), std::back_inserter(artistNames), [](const meta::Artist &artist) {
return QString::fromStdString(artist.name);
});
return artistNames.join(", ");
}
case IsCurrent:
return m_playlist.tracks[index.row()].video_id == m_currentVideoId.toStdString();
}
Q_UNREACHABLE();
@@ -72,12 +68,102 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const
return {};
}
QHash<int, QByteArray> PlaylistModel::roleNames() const
{
return {
{Title, "title"},
{VideoId, "videoId"},
{Artists, "artists"},
{IsCurrent, "isCurrent"}
};
}
QString PlaylistModel::initialVideoId() const
{
return m_initialVideoId;
}
void PlaylistModel::setInitialVideoId(const QString &videoId)
{
m_initialVideoId = videoId;
Q_EMIT initialVideoIdChanged();
}
bool PlaylistModel::loading() const
{
return m_loading;
}
void PlaylistModel::setLoading(bool loading)
{
m_loading = loading;
Q_EMIT loadingChanged();
}
QString PlaylistModel::nextVideoId() const
{
auto currentTrack = std::find_if(m_playlist.tracks.begin(), m_playlist.tracks.end(),
[=](const watch::Playlist::Track &track) {
return track.video_id == m_currentVideoId.toStdString();
});
if (currentTrack == m_playlist.tracks.end() || currentTrack + 1 == m_playlist.tracks.end()) {
return {};
}
return QString::fromStdString((currentTrack + 1)->video_id);
}
QString PlaylistModel::currentVideoId() const
{
return m_currentVideoId;
}
void PlaylistModel::setCurrentVideoId(const QString &videoId)
{
const auto old = m_currentVideoId;
m_currentVideoId = videoId;
emitCurrentVideoChanged(old);
Q_EMIT currentVideoIdChanged();
}
void PlaylistModel::next()
{
const auto old = m_currentVideoId;
m_currentVideoId = nextVideoId();
emitCurrentVideoChanged(old);
Q_EMIT currentVideoIdChanged();
}
void PlaylistModel::skipTo(const QString &videoId)
{
const auto old = m_currentVideoId;
m_currentVideoId = videoId;
emitCurrentVideoChanged(old);
Q_EMIT currentVideoIdChanged();
}
void PlaylistModel::emitCurrentVideoChanged(const QString &oldVideoId)
{
const auto oldVideoIt = std::find_if(m_playlist.tracks.begin(), m_playlist.tracks.end(),
[=](const watch::Playlist::Track &track) {
return track.video_id == oldVideoId.toStdString();
});
const auto currentVideoIt = std::find_if(m_playlist.tracks.begin(), m_playlist.tracks.end(),
[=](const watch::Playlist::Track &track) {
return track.video_id == m_currentVideoId.toStdString();
});
int oldIndex = std::distance(m_playlist.tracks.begin(), oldVideoIt);
int newIndex = std::distance(m_playlist.tracks.begin(), currentVideoIt);
dataChanged(index(oldIndex), index(oldIndex), {IsCurrent});
dataChanged(index(newIndex), index(newIndex), {IsCurrent});
}
QString PlaylistModel::playlistId() const
{
auto id = m_playlistId;
id.remove(QStringLiteral("VL")); // Workaround: get_watch_playlist only accepts
// the playlists without the leading VL
return id;
return m_playlistId;
}
void PlaylistModel::setPlaylistId(const QString &playlistId)
@@ -85,17 +171,3 @@ void PlaylistModel::setPlaylistId(const QString &playlistId)
m_playlistId = playlistId;
Q_EMIT playlistIdChanged();
}
QUrl PlaylistModel::thumbnailUrl() const
{
if (m_playlist.thumbnails.empty()) {
return QUrl();
}
return QUrl(QString::fromStdString(m_playlist.thumbnails.back().url));
}
QString PlaylistModel::title() const
{
return QString::fromStdString(m_playlist.title);
}
+38 -24
View File
@@ -1,52 +1,66 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QAbstractListModel>
#include <ytmusic.h>
#include "abstractytmusicmodel.h"
class PlaylistModel : public AbstractYTMusicModel
class PlaylistModel : public QAbstractListModel
{
Q_OBJECT
// input
Q_PROPERTY(QString playlistId READ playlistId WRITE setPlaylistId NOTIFY playlistIdChanged REQUIRED)
Q_PROPERTY(QString initialVideoId READ initialVideoId WRITE setInitialVideoId NOTIFY initialVideoIdChanged)
Q_PROPERTY(QString playlistId READ playlistId WRITE setPlaylistId NOTIFY playlistIdChanged)
// output
Q_PROPERTY(QUrl thumbnailUrl READ thumbnailUrl NOTIFY thumbnailUrlChanged)
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(bool loading READ loading WRITE setLoading NOTIFY loadingChanged)
Q_PROPERTY(QString currentVideoId READ currentVideoId NOTIFY currentVideoIdChanged)
public:
explicit PlaylistModel(QObject *parent = nullptr);
enum Role {
Title,
Artists,
Title = Qt::UserRole + 1,
VideoId,
ThumbnailUrl,
ArtistsDisplayString
Artists,
IsCurrent
};
explicit PlaylistModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
QString initialVideoId() const;
void setInitialVideoId(const QString &videoId);
Q_SIGNAL void initialVideoIdChanged();
bool loading() const;
void setLoading(bool loading);
Q_SIGNAL void loadingChanged();
QString nextVideoId() const;
QString currentVideoId() const;
void setCurrentVideoId(const QString &videoId);
Q_SIGNAL void currentVideoIdChanged();
Q_INVOKABLE void next();
Q_INVOKABLE void skipTo(const QString &videoId);
QString playlistId() const;
void setPlaylistId(const QString &playlistId);
Q_SIGNAL void playlistIdChanged();
QUrl thumbnailUrl() const;
Q_SIGNAL void thumbnailUrlChanged();
QString title() const;
Q_SIGNAL void titleChanged();
private:
QString m_playlistId;
void emitCurrentVideoChanged(const QString &oldVideoId);
playlist::Playlist m_playlist {};
QString m_initialVideoId;
QString m_playlistId;
QString m_currentVideoId;
bool m_loading = false;
watch::Playlist m_playlist;
};
-29
View File
@@ -1,29 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "playlistutils.h"
#include <QString>
#include <numeric>
#include <ytmusic.h>
namespace PlaylistUtils {
QString artistsToString(const std::vector<meta::Artist> &artists)
{
if (!artists.empty()) {
QString string = QString::fromStdString(artists.front().name);
std::for_each(artists.begin() + 1, artists.end(),
[&string](const meta::Artist &artist) {
string.append(QStringLiteral(", %1").arg(QString::fromStdString(artist.name)));
});
return string;
}
return {};
}
}
-16
View File
@@ -1,16 +0,0 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <vector>
class QString;
namespace meta {
struct Artist;
}
namespace PlaylistUtils {
QString artistsToString(const std::vector<meta::Artist> &artists);
};
-19
View File
@@ -1,27 +1,8 @@
<!--
SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-->
<RCC>
<qresource prefix="/">
<file alias="main.qml">contents/ui/main.qml</file>
<file alias="AlbumPage.qml">contents/ui/AlbumPage.qml</file>
<file alias="ArtistPage.qml">contents/ui/ArtistPage.qml</file>
<file alias="PlayerFooter.qml">contents/ui/PlayerFooter.qml</file>
<file alias="SearchPage.qml">contents/ui/SearchPage.qml</file>
<file alias="PlaylistPage.qml">contents/ui/PlaylistPage.qml</file>
<file alias="ConfirmationMessage.qml">contents/ui/ConfirmationMessage.qml</file>
<file alias="LibraryPage.qml">contents/ui/LibraryPage.qml</file>
<file alias="MinimizedPlayerControls.qml">contents/ui/MinimizedPlayerControls.qml</file>
<file alias="MaximizedPlayerPage.qml">contents/ui/MaximizedPlayerPage.qml</file>
<file>migrations/2022-05-20-194850_init/down.sql</file>
<file>migrations/2022-05-20-194850_init/up.sql</file>
<file alias="PlaybackHistory.qml">contents/ui/PlaybackHistory.qml</file>
<file alias="RoundedMask.qml">contents/ui/RoundedMask.qml</file>
<file alias="SongMenu.qml">contents/ui/SongMenu.qml</file>
<file alias="ListHeader.qml">contents/ui/ListHeader.qml</file>
<file alias="RoundedImage.qml">contents/ui/RoundedImage.qml</file>
</qresource>
</RCC>
+28 -108
View File
@@ -1,16 +1,15 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#include "searchmodel.h"
#include "playlistutils.h"
#include <ranges>
#include <QDebug>
SearchModel::SearchModel(QObject *parent)
: AbstractYTMusicModel(parent)
: QAbstractListModel(parent)
{
connect(this, &SearchModel::searchQueryChanged, this, [this] {
connect(this, &SearchModel::searchQueryChanged, this, [=] {
if (m_searchQuery.isEmpty()) {
beginResetModel();
m_searchResults.clear();
@@ -19,23 +18,23 @@ SearchModel::SearchModel(QObject *parent)
}
setLoading(true);
auto future = YTMusicThread::instance()->search(m_searchQuery);
connectFuture(future, this, [=, this](const std::vector<search::SearchResultItem> &results) {
beginResetModel();
setLoading(false);
m_searchResults = results;
endResetModel();
});
AsyncYTMusic::instance().search(m_searchQuery);
});
connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, [this] {
connect(&AsyncYTMusic::instance(), &AsyncYTMusic::searchFinished, this, [=](const std::vector<search::SearchResultItem> &results) {
beginResetModel();
setLoading(false);
m_searchResults = results;
endResetModel();
});
}
SearchModel::~SearchModel()
{
}
int SearchModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : int(m_searchResults.size());
return parent.isValid() ? 0 : m_searchResults.size();
}
QVariant SearchModel::data(const QModelIndex &index, int role) const
@@ -75,70 +74,6 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
Q_UNREACHABLE();
}
}, m_searchResults.at(index.row()));
case VideoId:
return std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, search::Album>) {
return QString();
} else if constexpr (std::is_same_v<T, search::Artist>) {
return QString();
} else if constexpr (std::is_same_v<T, search::Playlist>) {
return QString();
} else if constexpr (std::is_same_v<T, search::Song>) {
return QString::fromStdString(arg.video_id);
} else if constexpr (std::is_same_v<T, search::Video>) {
return QString::fromStdString(arg.video_id);
} else {
Q_UNREACHABLE();
}
}, m_searchResults.at(index.row()));
case Artists:
return QVariant::fromValue(std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, search::Album>) {
return std::vector<meta::Artist>();
} else if constexpr (std::is_same_v<T, search::Artist>) {
return std::vector<meta::Artist>();
} else if constexpr (std::is_same_v<T, search::Playlist>) {
return std::vector<meta::Artist>();
} else if constexpr (std::is_same_v<T, search::Song>) {
return arg.artists;
} else if constexpr (std::is_same_v<T, search::Video>) {
return arg.artists;
} else {
Q_UNREACHABLE();
}
}, m_searchResults.at(index.row())));
case RadioPlaylistId:
return std::visit([&](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, search::Artist>) {
if (arg.radio_id) {
return QString::fromStdString(*arg.radio_id);
}
}
return QString();
}, m_searchResults.at(index.row()));
case ThumbnailUrl:
return std::visit([&](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if (!arg.thumbnails.empty()) {
return QString::fromStdString(arg.thumbnails.front().url);
}
return QString();
}, m_searchResults.at(index.row()));
case ArtistsDisplayString:
return QVariant::fromValue(std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, search::Song> || std::is_same_v<T, search::Video> || std::is_same_v<T, search::Album>) {
return PlaylistUtils::artistsToString(arg.artists);
}
return QString();
}, m_searchResults.at(index.row())));
}
Q_UNREACHABLE();
@@ -150,12 +85,7 @@ QHash<int, QByteArray> SearchModel::roleNames() const
{
return {
{Title, "title"},
{Type, "type"},
{VideoId, "videoId"},
{Artists, "artists"},
{ArtistsDisplayString, "artistsDisplayString"},
{RadioPlaylistId, "radioPlaylistId"},
{ThumbnailUrl, "thumbnailUrl"}
{Type, "type"}
};
}
@@ -170,6 +100,17 @@ void SearchModel::setSearchQuery(const QString &searchQuery)
Q_EMIT searchQueryChanged();
}
bool SearchModel::loading() const
{
return m_loading;
}
void SearchModel::setLoading(bool loading)
{
m_loading = loading;
Q_EMIT loadingChanged();
}
void SearchModel::triggerItem(int row)
{
std::visit([&](auto&& arg) {
@@ -177,36 +118,15 @@ void SearchModel::triggerItem(int row)
if constexpr (std::is_same_v<T, search::Album>) {
Q_EMIT openAlbum(QString::fromStdString(arg.browse_id));
} else if constexpr (std::is_same_v<T, search::Artist>) {
Q_EMIT openArtist(QString::fromStdString(arg.browse_id), QString::fromStdString(arg.radio_id.value_or("")), QString::fromStdString(arg.shuffle_id.value_or("")));
Q_EMIT openArtist(QString::fromStdString(arg.browse_id));
} else if constexpr (std::is_same_v<T, search::Playlist>) {
Q_EMIT openPlaylist(QString::fromStdString(arg.browse_id));
} else if constexpr (std::is_same_v<T, search::Song>) {
Q_EMIT openSong(QString::fromStdString(arg.video_id));
} else if constexpr (std::is_same_v<T, search::Video>) {
Q_EMIT openVideo(QString::fromStdString(arg.video_id), QString::fromStdString(arg.title));
Q_EMIT openVideo(QString::fromStdString(arg.video_id));
} else {
Q_UNREACHABLE();
}
}, m_searchResults.at(row));
}
int SearchModel::itemType(search::SearchResultItem const &item)
{
return std::visit([&](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr(std::is_same_v<T, search::Album>) {
return SearchModel::Type::Album;
} else if constexpr(std::is_same_v<T, search::Artist>) {
return SearchModel::Type::Artist;
} else if constexpr(std::is_same_v<T, search::Playlist>) {
return SearchModel::Type::Playlist;
} else if constexpr(std::is_same_v<T, search::Song>) {
return SearchModel::Type::Song;
} else if constexpr(std::is_same_v<T, search::Video>) {
return SearchModel::Type::Video;
} else {
Q_UNREACHABLE();
}
}, item);
}
+12 -12
View File
@@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 Jonah Brüchert <[email protected]>
//
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,18 +8,17 @@
#include <QThread>
#include "asyncytmusic.h"
#include "abstractytmusicmodel.h"
class SearchModel : public AbstractYTMusicModel
class SearchModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString searchQuery READ searchQuery WRITE setSearchQuery NOTIFY searchQueryChanged)
Q_PROPERTY(bool loading READ loading WRITE setLoading NOTIFY loadingChanged)
public:
enum Type {
Artist,
ArtistsDisplayString,
Album,
Playlist,
Song,
@@ -29,14 +28,11 @@ public:
enum Role {
Title = Qt::UserRole + 1,
Type,
VideoId,
Artists,
RadioPlaylistId,
ThumbnailUrl
Type
};
explicit SearchModel(QObject *parent = nullptr);
~SearchModel();
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
@@ -46,16 +42,20 @@ public:
void setSearchQuery(const QString &searchQuery);
Q_SIGNAL void searchQueryChanged();
bool loading() const;
void setLoading(bool loading);
Q_SIGNAL void loadingChanged();
Q_INVOKABLE void triggerItem(int row);
Q_SIGNAL void openAlbum(const QString &browseId);
Q_SIGNAL void openArtist(const QString &browseId,const QString &radioId, const QString &shuffleId);
Q_SIGNAL void openArtist(const QString &browseId);
Q_SIGNAL void openPlaylist(const QString &browseId);
Q_SIGNAL void openSong(const QString &videoId);
Q_SIGNAL void openVideo(const QString &videoId, const QString &title);
Q_SIGNAL void openVideo(const QString &videoId);
private:
QString m_searchQuery;
std::vector<search::SearchResultItem> m_searchResults;
static int itemType(search::SearchResultItem const &item);
bool m_loading = false;
};
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more