Repository: neithern/g4music Branch: master Commit: e5d5465de4a4 Files: 99 Total size: 890.2 KB Directory structure: gitextract_swy9s77r/ ├── .gitignore ├── .vscode/ │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── COPYING ├── README.md ├── data/ │ ├── app.desktop.in │ ├── app.gschema.xml │ ├── app.metainfo.xml.in │ ├── app.service.in │ ├── icons/ │ │ └── meson.build │ └── meson.build ├── g4music.doap ├── meson.build ├── pkgs/ │ └── flatpak/ │ └── com.github.neithern.g4music.json ├── po/ │ ├── LINGUAS │ ├── POTFILES.in │ ├── UPDATE.md │ ├── be.po │ ├── bg.po │ ├── cs.po │ ├── da.po │ ├── de.po │ ├── el.po │ ├── es.po │ ├── et.po │ ├── eu.po │ ├── fa.po │ ├── fi.po │ ├── fr.po │ ├── g4music.pot │ ├── he.po │ ├── hi.po │ ├── hu.po │ ├── ia.po │ ├── id.po │ ├── it.po │ ├── ja.po │ ├── ka.po │ ├── meson.build │ ├── nl.po │ ├── oc.po │ ├── pt_BR.po │ ├── ro.po │ ├── ru.po │ ├── sk.po │ ├── sl.po │ ├── sv.po │ ├── tr.po │ ├── uk.po │ ├── zh_CN.po │ └── zh_TW.po └── src/ ├── action-handles.vala ├── application.vala ├── config.vapi ├── gresource.xml ├── gst/ │ ├── ape-demux.c │ ├── gst-ext.h │ ├── gst-ext.vapi │ ├── gst-player.vala │ ├── peak-calculator.vala │ └── tag-parser.vala ├── gtk/ │ ├── help-overlay.ui │ ├── play-panel.ui │ ├── preferences.ui │ ├── store-panel.ui │ └── style.css ├── main.vala ├── meson.build ├── ui/ │ ├── dialogs.vala │ ├── leaflet.vala │ ├── mini-bar.vala │ ├── music-list.vala │ ├── music-widgets.vala │ ├── narrow-bar.vala │ ├── paintables.vala │ ├── peak-bar.vala │ ├── play-bar.vala │ ├── play-panel.vala │ ├── playlist-dialog.vala │ ├── preferences.vala │ ├── stable-label.vala │ ├── store-panel.vala │ ├── taglist-dialog.vala │ ├── volume-button.vala │ └── window.vala └── utils/ ├── async-task.vala ├── cover-cache.vala ├── data-io.vala ├── dir-cache.vala ├── dir-monitor.vala ├── mpris.vala ├── music-library.vala ├── music-loader.vala ├── music.vala ├── playlist-file.vala ├── portal.vala ├── tag-cache.vala └── thumbnailer.vala ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .flatpak-builder build ================================================ FILE: .vscode/launch.json ================================================ { // Debug with LLDB DAP extension "version": "0.2.0", "configurations": [ { "type": "lldb-dap", "request": "launch", "name": "Launch", "program": "${workspaceFolder}/build/src/${workspaceFolderBasename}", "cwd": "${workspaceFolder}", "preLaunchTask": "Compile" } ] } ================================================ FILE: .vscode/settings.json ================================================ { "mesonbuild.configureOnOpen": true, "lldb.showDisassembly": "auto", "lldb.dereferencePointers": true, "lldb.consoleMode": "commands" } ================================================ FILE: .vscode/tasks.json ================================================ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "Compile", "type": "shell", "command": "meson compile -C build", "problemMatcher": [], "group": { "kind": "build", "isDefault": true } }, { "label": "Setup", "type": "shell", "command": "meson setup build --reconfigure", } ] } ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . ================================================ FILE: README.md ================================================ Project logo # Gapless Play your music elegantly. Gapless (AKA: G4Music) is a light weight music player written in GTK4, focuses on large music collection. ## Features - Supports most music file types, Samba and any other remote protocols (depends on GIO and GStreamer). - Fast loading and parsing thousands of music files in very few seconds, monitor local changes. - Low memory usage for large music collection with album covers (embedded and external), no thumbnail caches to store. - Group and sorts by album/artist/title, shuffle list, full-text searching. - Fluent adaptive user interface for different screen (Desktop, Tablet, Mobile). - Gaussian blurred cover as background, follows GNOME light/dark mode. - Supports creating and editing playlists, drag cover to change order or add to another playlist. - Supports drag and drop with other apps. - Supports audio peaks visualizer. - Supports gapless playback. - Supports normalizing volume with ReplayGain. - Supports specified audio sink. - Supports MPRIS control. ## Install from Flathub ## Install from Snapcraft (unofficial) Get it from the Snap Store ## FreeBSD Dependencies ```bash pkg install vala meson libadwaita gstreamer1-plugins-all gettext gtk4 ``` ## How to build It is written in Vala, simple and clean code, with few third-party dependencies: 1. Clone the code from gitlab. 2. Install vala, develop packages of gtk4, libadwaita, gstreamer. 3. Run in the project directory: `meson setup build --buildtype=release` `meson install -C build` ## Change Log Check the [release tags](https://gitlab.gnome.org/neithern/g4music/-/tags) for change log. ================================================ FILE: data/app.desktop.in ================================================ [Desktop Entry] Name=Gapless GenericName=Music Player Comment=Play your music elegantly Exec=g4music %U Icon=@app_id@ Terminal=false Type=Application Categories=GNOME;GTK;Music;Audio;AudioVideo; MimeType=audio/wav;audio/webm;audio/x-aac;audio/x-aiff;audio/x-ape;audio/x-flac;audio/x-it;audio/x-m4a;audio/x-m4b;audio/x-matroska;audio/x-mod;audio/x-mp1;audio/x-mp2;audio/x-mp3;audio/x-mpg;audio/x-mpeg;audio/x-ms-asf;audio/x-ms-asx;audio/x-ms-wax;audio/x-ms-wma;audio/x-musepack;audio/x-opus+ogg;audio/x-pn-aiff;audio/x-pn-au;audio/x-pn-realaudio;audio/x-pn-realaudio-plugin;audio/x-pn-wav;audio/x-pn-windows-acm;audio/x-realaudio;audio/x-real-audio;audio/x-s3m;audio/x-sbc;audio/x-shorten;audio/x-speex;audio/x-stm;audio/x-tta;audio/x-wav;audio/x-wavpack;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-xm;audio/x-mpegurl;audio/x-scpls; Keywords=music;sound;player;media;audio;playlist; StartupNotify=true DBusActivatable=true X-SingleMainWindow=true X-Purism-FormFactor=Workstation;Mobile; ================================================ FILE: data/app.gschema.xml ================================================ 1 Background blur mode 0 Prefer color scheme false Compact playlist view true Grid view for artists/albums false Monitor local file changes '' Load music from folder false Load thumbnails for non-local files false Show remain progress '' Prefer audio sink false Keep playing after window closed true Enable gapless playback 0 Normalize volume with ReplayGain true Rotate album cover '=' Peak characters true Show audio peak level true Single click to activate item 3 Sort Mode 0 false 800 600 '' '' '' 1 ================================================ FILE: data/app.metainfo.xml.in ================================================ @app_id@ Gapless CC0-1.0 GPL-3.0-or-later Play your music elegantly Nanling neithern@outlook.com https://gitlab.gnome.org/neithern/g4music https://gitlab.gnome.org/neithern/g4music/issues https://l10n.gnome.org/module/g4music @app_id@.desktop @app_id@.desktop g4music

Gapless (AKA: G4Music) is a light weight music player written in GTK4, focuses on large music collection.

Features

  • Supports most music file types, Samba and any other remote protocols (depends on GIO and GStreamer).
  • Fast loading and parsing thousands of music files in very few seconds, monitor local changes.
  • Low memory usage for large music collection with album covers (embedded and external), no thumbnail caches to store.
  • Group and sorts by album/artist/title, shuffle list, full-text searching.
  • Fluent adaptive user interface for different screen (Desktop, Tablet, Mobile).
  • Gaussian blurred cover as background, follows GNOME light/dark mode.
  • Supports creating and editing playlists, drag cover to change order or add to another playlist.
  • Supports drag and drop with other apps.
  • Supports audio peaks visualizer.
  • Supports gapless playback.
  • Supports normalizing volume with ReplayGain.
  • Supports specified audio sink.
  • Supports MPRIS control.
#d6f6d6 #266626 Main window https://gitlab.gnome.org/neithern/screenshots/-/raw/main/g4music/window.png Albums view https://gitlab.gnome.org/neithern/screenshots/-/raw/main/g4music/albums.png Playing view https://gitlab.gnome.org/neithern/screenshots/-/raw/main/g4music/playing.png Playlist view https://gitlab.gnome.org/neithern/screenshots/-/raw/main/g4music/playlist.png
  • Get filtered items as a playlist.
  • Upgrade to GNOME 49.
  • Add translation: Interlingua, Greek.
  • Update Hungarian translation.
  • Always insert to the next position when play an album.
  • Click index always to open the playing page.
  • Load the library into the queue only when it is empty.
  • Click index to open playing page, restore on next startup.
  • Set the main window as parent.
  • Drop MPRIS permission of flatpak.
  • Upgrade to GNOME 48.
  • Update Danish translation.
  • Remove unnecessary flatpak permissions.
  • Add Bulgarian translation
  • Update translation: Danish, Persian, Hebrew.
  • Optimize scrolling to current item at startup.
  • Update translation: Swedish, Finnish, Occitan, Turkish.
  • Optimize relative file paths for playlists.
  • Start drag only when the pointer inside the cover image.
  • Show the full date of the album if possible.
  • Build with low version of vala compiler.
  • Update translation: Slovak, German, Dutch, Occitan.
  • Use the main queue as the actual "Now Playing" and "Playing History".
  • New style for the number of files being dragging.
  • Optimize updating the library.
  • Support volume for mpris.
  • Update translation: German, Russian, Georgian, Belarusian.

New features:

  • Add music to playlist, edit/rename/remove existing playlists.
  • Drag cover image to re-arrange in a playlist, add to another playlist, or share to another app.
  • Insert music files to playlist drag from another app.
  • Show detail tags in a dialog.
  • Move music/playlist file to trash.
  • New option: single click to activate item.

Breaking changes:

  • Long press an item to enter multiple selection mode.
  • The main queue is editable and recoverable on next startup.
  • Except the main queue, albums/playlists are no longer sort-able, but can be random played.
  • Won't switch playing list unless activate manually or play end, open on next startup.
  • Parse ORIGINALDATE/YEAR from Extended-Comment as album's date.
  • Full support of color scheme: System/Light/Dark.
  • Don't enable pipewire manually, override the rank if still want to use it.

Flatpak changes:

  • Request writable permission of Music folder for editing playlists.
  • Upgrade to GNOME 47.

Optimizations:

  • Improve performance of loading large playlist.
  • Group popover menu actions.
  • Many other optimizations by code refactoring.
  • Fix audio seeking stutter, thanks to @dov-vai.
  • Load last played file first before load other files.
  • Update the library pages when file changes.
  • Many UI optimizations.
  • Update translation: Occitan, Persian, Hebrew, Russian, Georgian.
  • Play the next album after end of an album.
  • Optimize UI layout, shrink the window.
  • Fix crash when open an album with invalid utf-8 name.
  • Update translation: Ukrainian, Slovenian, German, Slovenian, Belarusian, Chinese.
  • Optimize UI layout and style for small screen.
  • Optimize loading cover of playing music.
  • Optimize pressing Space to toggle play/pause.
  • Update translation: Chinese, German.
  • Fix initial view when no music.
  • Fix Drag-drop when no music.
  • Update translation: Hindi, Occitan, Persian, Brazilian Portuguese, Hungarian, Swedish, Turkish, Belarusian.
  • Fix build issue before GTK 4.10.
  • Remove screen shots from flatpak package.
  • Update translation: Russian, Slovenian, Ukrainian.
  • Rename to 'Gapless' to follow GNOME HIG.
  • Marquee animation for long title.
  • Fix cursor for clickable label.
  • Tweak blur background.
  • Add Hebrew translation.
  • Update Occitan translation.
  • Drag the play-panel to move the window.
  • Individual sort-mode for albums.
  • show indicator and scroll to the playing artist/album.
  • Save and restore window's maximized.
  • Many other UI improvement and fixing.
  • Use playbin3 if gstreamer>=1.24.
  • Optimize pressing Space to play/pause.
  • Add translation: Persian.
  • Press Space to toggle play/pause.
  • Add Position property for MPRIS.
  • Don't change playing music after reload library.
  • Update current item when items changed.
  • Free memory when music be removed.
  • Add translation: Georgian, Hindi.
  • Update translation: Spanish, Slovenian.
  • Fix "playing" state for music entries.
  • Use Overlay to avoid changing list's width.
  • Sorting albums by Disc Number.
  • Add translation: Romanian, Danish, Slovenian.
  • Update translation: Belarusian, Occitan.
  • Build flatpak with GNOME 45.
  • Fix style of switch bar.
  • Fix loading M3U playlist.
  • Add Hungarian translation.
  • Update Swedish translation.
  • Make seek_bar auto width.
  • UI improvement and fixing.
  • Add translation: Ukrainian, Finnish, Indonesian, Basque.
  • Update translation: Spanish, French, German, Turkish, Russian.
  • Option: Grid view for artists/albums.
  • Bigger image size and column width for grid view.
  • UI improvement and fixing.
  • Group by album-artist, sort albums by year.
  • Save and restore the last library view.
  • Open a new page when play all music of an artist.
  • Stay in the current list when playing the whole album.
  • Show the local playlists.
  • Optimize searching.
  • Update German and Japanese translation.
  • Group artists/albums as library.
  • Optimize layout for landscape view.
  • Show progressbar when loading.
  • Add icons for preference rows.
  • Optimize seeking.
  • Add Japanese translation, thanks to @Gnuey56.
  • App icon refresh from @ddaudix.
  • Add a sort mode by: Artist/Album
  • Drag-drop folders works from Files.
  • Rotating cover sync with play progress, fix issues.
  • Fix gapless related issues.
  • Update German translation.
  • Support .m3u/.pls playlist from command line.
  • Modeless preferences window.
  • UI improvement and fixing.
  • Update Spanish translation.
  • Option: rotate cover when playing.
  • Click music title to search.
  • Revert to single page of preferences.
  • Add 2 shortcut keys.
  • UI improvement and fixing.
  • Update German translation.
  • App startup acceleration.
  • Option: Prefer audio sink.
  • Replay-gain supports album mode.
  • Show same album cover in app and MPRIS.
  • Option: Monitor local file changes.
  • Right click or long press to show popover menu.
  • Scroll playlist smoothly.
  • Many UI improvements.
  • Optimize searching by album/artist/title.
  • Optimize loading files and cache.
  • Update German translation.
  • Option: compact playlist view.
  • Option: custom characters for audio peaks.
  • Optimize playlist text size.
  • Optimize for macOS.
  • Save memory and CPU usage.
  • New style for no-cover images.
  • UI improvement and fixing.
  • MPRIS improvement.
  • UI improvement and fixing.
  • Build flatpak with GNOME 44.
  • MPRIS improvement.
  • UI improvement and fixing.
  • Update Spanish translation.
  • Fix wrong icon name of repeat-song.
  • Fix wrong state of Switch buttons.
  • Optimize loading thumbnails.
  • UI improvement and fixing.
  • Update German translation.
  • Select a music file to "Play at Next".
  • Lazy load thumbnails to faster startup and saving memory.
  • UI improvement and fixing.
  • Add Occitan translation, thanks to Quentin PAGÈS.
  • Build flatpak with GNOME 43.
  • Cache directories for faster loading.
  • Show peaks more smoothly.
  • Update Spanish and German translation, thanks to the translators.
  • v1.8.2: Build with vala 0.54.
  • Start to play when music changed.
  • Optimize audio peaks visualizer.
  • New style of popover menu.
  • Update many translation, thanks to the translators.
  • Option: Background blur mode.
  • Shortcut key for toggle sort.
  • Don't changing current song when searching.
  • Add Estonian translation, thanks to Henri.
  • Add Traditional Chinese translation, thanks to Julian.
  • v1.6.1: Fix wrong sort order of cached tags.
  • Show loading songs progress with percent.
  • Add sort by recently added, thanks to Mek101.
  • Add German translation, thanks to Jürgen Benvenuti.
  • Bug fix and UI improvement.
  • Popover menu for song entry.
  • Show initial status if no sound found.
  • Try parse album and track from file path.
  • Add Italian translation, thanks to Albano Aattistella.
  • App inhibit suspend to keep playing.
  • Performance improvement and memory saving.
  • Parse tags of MKV/MKA.
  • Add French translation, thanks to Aurélien Hamy.
  • Add Dutch translation, thanks to Heimen Stoffels.
  • Faster parsing OGG and MP4 tags.
  • Faster drawing texts using pango.
  • Many UI optimizations.
  • Add Brazilian Portuguese translation, thanks to @PedroHSilva.
  • Option: Gapless playback.
  • Make label clickable instead of hypelinks.
  • Add Turkish translation, thanks to @Sabri Ünal.
  • Fix cover image issues.
  • Fix saving tag cache issues.
  • Support ReplayGain to normalize volume.
  • Option: Keep playing after window closed.
  • Cache tags to make next loading faster.
  • Share album cover with same artist.
  • Fix some UI issues.
  • v1.0.1: Updated Russian translation.
  • Scale album cover to fix large window.
  • Navigate back if start search and folded.
  • Draw peak to avoid resizing the window.
  • Request host file permission in flatpak.
  • Show current song in Files.
  • Sort by track number in an album.
  • Sort text by prefix number.
  • Always follow sym-links when enumerate a folder.
  • Add spanish translation, thanks to @Radi4Ever.
  • Parallel parsing tags to make loading songs even faster.
  • Merge all kinds of tags as possible.
  • Fix crash when parsing some FLAC files, thanks to @GeoffreyCoulaud.
  • Use Adw.Leaflet to adapt to PC and phone screens.
  • Show a mini play bar when only the playlist on the window.
  • Show a spinner when loading songs.
  • Save and restore the window size.
  • Add Swedish translation, thanks to Åke Engelbrektson.
  • Faster parsing FLAC tags.
  • Detect music file type to fix incorrect parsing.
  • Update app icon to follow GNOME HIG, thanks to @daudix-UFO.
  • Add Russian translation, thanks to @daudix-UFO.
  • Change "Force Dark" to "Prefer Dark".
  • Recognize external images as cover art, supports transparency.
  • Showing peak now works with pipewire in flatpak.
  • Don't support tracker_sparql because it is slow.
  • Sort songs by Album/Artist/Title.
  • Option: Force dark theme.
  • Optimized for responsiveness.
  • Add volume button.
  • Repeat single song mode.
  • Supports pipewire for flatpak.
  • Supports parsing GIO file tags for flatpak.
  • Press any key to search, ESC to quit search.
  • Show abbreviation as MPRIS's cover if no cover image.

First release.

360 keyboard pointing touch
================================================ FILE: data/app.service.in ================================================ [D-BUS Service] Name=@app_id@ Exec=@bindir@/g4music --gapplication-service ================================================ FILE: data/icons/meson.build ================================================ scalable_dir = join_paths('hicolor', 'scalable', 'apps') scalable_conf = configure_file( input: join_paths(scalable_dir, 'app.svg'), output: '@0@.svg'.format(app_id), configuration: conf ) install_data( scalable_conf, install_dir: join_paths(get_option('datadir'), 'icons', scalable_dir) ) symbolic_dir = join_paths('hicolor', 'symbolic', 'apps') symbolic_conf = configure_file( input: join_paths(symbolic_dir, 'app-symbolic.svg'), output: '@0@-symbolic.svg'.format(app_id), configuration: conf ) install_data( symbolic_conf, install_dir: join_paths(get_option('datadir'), 'icons', symbolic_dir) ) ================================================ FILE: data/meson.build ================================================ conf = configuration_data() conf.set('app_id', app_id) conf.set('version', version) conf.set('bindir', get_option('prefix') / get_option('bindir')) desktop_conf = configure_file( input: 'app.desktop.in', output: '@0@.desktop.in'.format(app_id), configuration: conf ) desktop_file = i18n.merge_file( input: desktop_conf, output: '@0@.desktop'.format(app_id), type: 'desktop', po_dir: '../po', install: true, install_dir: get_option('datadir') / 'applications' ) desktop_utils = find_program('desktop-file-validate', required: false) if desktop_utils.found() test('Validate desktop file', desktop_utils, args: [desktop_file] ) endif service_file = configure_file( input: 'app.service.in', output: '@0@.service'.format(app_id), configuration: conf, ) install_data( service_file, install_dir: get_option('datadir') / 'dbus-1' / 'services' ) appstream_conf = configure_file( input: 'app.metainfo.xml.in', output: '@0@.metainfo.xml.in'.format(app_id), configuration: conf ) appstream_file = i18n.merge_file( input: appstream_conf, output: '@0@.metainfo.xml'.format(app_id), po_dir: '../po', install: true, install_dir: get_option('datadir') / 'metainfo' ) appstreamcli = find_program('appstreamcli', required: false) if not appstreamcli.found() appstreamcli = find_program('appstream-util', required: false) endif if appstreamcli.found() test('Validate appstream file', appstreamcli, args: ['validate', '--no-net', '--explain', appstream_file], suite: ['lint'], ) endif gschema_file = configure_file( input: 'app.gschema.xml', output: app_id + '.gschema.xml', configuration: conf, ) install_data( gschema_file, install_dir: get_option('datadir') / 'glib-2.0' / 'schemas' ) compile_schemas = find_program('glib-compile-schemas', required: false) if compile_schemas.found() test('Validate schema file', compile_schemas, args: ['--strict', '--dry-run', meson.current_source_dir()] ) endif subdir('icons') ================================================ FILE: g4music.doap ================================================ Gapless Play your music elegantly A fast, fluent, light weight music player written in GTK4, with a beautiful, adaptive user interface. Vala GTK 4 Libadwaita Nanling Zheng neithern ================================================ FILE: meson.build ================================================ project( 'g4music', [ 'vala', 'c' ], version: '4.6', license: 'GPL3' ) app_id = 'com.github.neithern.' + meson.project_name() version = meson.project_version() gnome = import('gnome') i18n = import('i18n') c_compiler = meson.get_compiler('c') vala_compiler = meson.get_compiler('vala') add_project_arguments('-DGETTEXT_PACKAGE="' + meson.project_name() + '"', language: 'c') add_project_arguments('--enable-experimental-non-null', language: 'vala') if vala_compiler.version().version_compare('>=0.56.10') add_project_arguments('--define=VALA_56_10', language: 'vala') endif gtk_dep = dependency('gtk4') if gtk_dep.version().version_compare('>=4.8') and vala_compiler.version().version_compare('>=0.56.6') add_project_arguments('--define=GTK_4_8', language: 'vala') endif if gtk_dep.version().version_compare('>=4.10') and vala_compiler.version().version_compare('>=0.56.6') add_project_arguments('--define=GTK_4_10', language: 'vala') endif if gtk_dep.version().version_compare('>=4.12') add_project_arguments('--define=GTK_4_12', language: 'vala') endif adw_dep = dependency('libadwaita-1') if adw_dep.version().version_compare('>=1.2') add_project_arguments('--define=ADW_1_2', language: 'vala') endif if adw_dep.version().version_compare('>=1.3') add_project_arguments('--define=ADW_1_3', language: 'vala') endif if adw_dep.version().version_compare('>=1.4') add_project_arguments('--define=ADW_1_4', language: 'vala') endif if adw_dep.version().version_compare('>=1.5') add_project_arguments('--define=ADW_1_5', language: 'vala') endif if adw_dep.version().version_compare('>=1.6') add_project_arguments('--define=ADW_1_6', language: 'vala') endif libm_dep = c_compiler.find_library('m', required: false) posix_dep = vala_compiler.find_library('posix', required: false) dependencies = [ dependency('gstreamer-1.0'), dependency('gstreamer-tag-1.0'), adw_dep, libm_dep, posix_dep, ] subdir('data') subdir('po') subdir('src') gnome.post_install( glib_compile_schemas: true, gtk_update_icon_cache: true, update_desktop_database: true, ) ================================================ FILE: pkgs/flatpak/com.github.neithern.g4music.json ================================================ { "app-id": "com.github.neithern.g4music", "runtime": "org.gnome.Platform", "runtime-version": "49", "sdk": "org.gnome.Sdk", "command": "g4music", "finish-args": [ "--device=dri", "--share=ipc", "--share=network", "--socket=fallback-x11", "--socket=pulseaudio", "--socket=wayland", "--filesystem=xdg-music", "--talk-name=org.gtk.vfs.*" ], "cleanup": [ "/include", "/lib/pkgconfig", "/man", "/share/doc", "/share/gtk-doc", "/share/man", "/share/pkgconfig", "/share/vala", "*.la", "*.a" ], "modules": [ { "name": "g4music", "builddir": true, "buildsystem": "meson", "sources": [ { "type": "dir", "path": "../../" } ] } ] } ================================================ FILE: po/LINGUAS ================================================ be bg cs da de el es et eu fa fi fr he hi hu ia id it ja ka nl oc pt_BR ro ru sk sl sv tr uk zh_CN zh_TW ================================================ FILE: po/POTFILES.in ================================================ data/app.desktop.in data/app.metainfo.xml.in src/gtk/help-overlay.ui src/gtk/play-panel.ui src/gtk/preferences.ui src/gtk/store-panel.ui src/action-handles.vala src/application.vala src/main.vala src/ui/dialogs.vala src/ui/music-list.vala src/ui/music-widgets.vala src/ui/play-bar.vala src/ui/play-panel.vala src/ui/playlist-dialog.vala src/ui/preferences.vala src/ui/store-panel.vala src/ui/taglist-dialog.vala src/ui/volume-button.vala src/ui/window.vala src/utils/music.vala ================================================ FILE: po/UPDATE.md ================================================ ### Run at build directory: 1. Update main pot file: `meson compile g4music-pot` 2. Update .po files: `meson compile g4music-update-po` ================================================ FILE: po/be.po ================================================ # Belarusian translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Yuras Shumovich , 2023. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-22 16:13+0300\n" "Last-Translator: Yuras Shumovich \n" "Language-Team: Belarusian \n" "Language: be\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%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Прайгравальнік музыкі" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Вытанчанае прайграванне музыкі" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;музыка;гук;прайгравальнік;плэер;" "мультымедыя;аўдыя;спіс прайгравання;плэйліст;плэй-ліст;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (таксама вядомы як G4Music) легкаважны прайгравальнік музыкі, які " "напісаны на GTK4 і накіраваны на працу з вялікімі аўдыякалекцыямі." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Магчымасці" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Падтрымка вялікай колькасці аўдыяфарматаў, аддаленага пратакола Samba і " "іншых (у залежнасці ад GIO і GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Хуткая загрузка і аналіз тысяч аўдыяфайлаў за лічаныя секунды, назіранне за " "змяненнем лакальных файлаў." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Нізкае выкарыстанне памяці для вялікіх аўдыякалекцый з вокладкамі альбомаў " "(убудаваных і знешніх) без захавання кэша мініяцюр." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Групаванне і сартаванне альбомаў/выканаўцаў/назваў, выпадковы парадак, " "паўнатэкставы пошук." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Адаптыўны інтэрфейс карыстальніка, прыдатны для розных памераў экрана (ПК, " "Планшэт, Мабільныя прылады)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Фон з размытай вокладкі, змяненне светлага і цёмнага рэжыму згодна з " "наладамі сістэмы." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Стварэнне і рэдагаванне спісаў прайгравання, перацягванне вокладкі для " "змянення парадку або дадавання ў іншы спіс прайгравання." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Перацягванне з іншых праграм." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Візуалізацыя пікавага ўзроўню гуку." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Бесперапыннае прайграванне." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Нармалізацыя гучнасці праз ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Вызначэнне прыёмнікаў аўдыясігналу." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Кіраванне праз MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Галоўнае акно" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Акно «Альбомы»" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Акно «Прайграванне»" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Акно «Спіс прайгравання»" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Агульныя" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Паказаць спалучэнні клавіш" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Налады" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Выйсці" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Паказаць тэгі" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Прайграванне" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Прайграванне/паўза" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Наступная дарожка" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Папярэдняя дарожка" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Спіс прайгравання" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Пошук" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Пераключыць сартаванне" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Абнавіць бібліятэку" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Захаваць спіс" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Назад" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Агульныя" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Рэжым размывання фону" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Кампактны спіс прайгравання" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Рэжым сеткі для выканаўцаў/альбомаў" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Актывацыя элемента адзінарным націсканнем мышы" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Працягваць прайграванне пасля закрыцця акна" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Бібліятэка" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Загружаць музыку з папкі" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Сачыць за змяненнем лакальных файлаў" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Загружаць мініяцюры для не лакальных файлаў" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Можа выклікаць празмернае выкарыстанне і запавольванне працы сеткі" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Прайграванне" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Паказваць пікавы ўзровень гуку" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Сімвалы для вываду" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Кружэнне вокладкі альбома" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Уключыць бесперапыннае прайграванне" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Нармалізацыя гучнасці праз ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Пераважны прыёмнік сігналу GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Галоўнае меню" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Пошук" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Сартаванне" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "Схема _колераў" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "Сіс_тэмная" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Светлая" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Цёмная" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "_Абнавіць бібліятэку" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Выбраць некалькі" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Захаваць спіс" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Налады" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Спалучэнні клавіш" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Аб праграме" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Альбом" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Выканаўца" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Выканаўца/Альбом" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Назва" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Нядаўнія" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Выпадковы парадак" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Файлы выяў" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Вокладка экспартавана" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Не ўдалося экспартаваць вокладку" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Не ўдалося захаваць спіс прайгравання" #: src/application.vala:447 msgid "Playlist Files" msgstr "Файлы спісаў прайгравання" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Спіс прайгравання захаваны" #: src/application.vala:597 msgid "Keep playing" msgstr "Працягваць прайграванне" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Хуткі плаўны і лёгкі прайгравальнік музыкі напісаны на аснове GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Юрась Шумовіч " #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Не" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Так" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Спіс прайгравання зменены, захаваць яго?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Паставіць _наступнай" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Дадаць у _чаргу прайгравання" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Дадаць у _спіс прайгравання…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Выдаліць" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Перамясціць у сметніцу" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Выбраць усе" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Паставіць наступнай" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Дадаць у чаргу прайгравання" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Дадаць у спіс прайгравання" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Выдаліць" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Прайграць" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Выпадковае прайграванне" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Паказаць _файл спіса" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Пошук па назве" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Пошук па альбому" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Пошук па выканаўцу" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Паказаць файл _вокладкі" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Экспартаваць вокладку" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Паказаць _тэгі…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Паказаць размяшчэнне _файла" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Паўтор адной песні" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Папярэдняя дарожка" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Прайграванне/паўза" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Наступная дарожка" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Перацягніце сюды аўдыяфайлы або \n" "змяніце месца размяшчэння музыкі: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Новы спіс прайгравання" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "У «%s» спісы прайгравання не знойдзены" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Ніколі" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Заўсёды" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Толькі з вокладкай" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Дарожка" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Прайграванне" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Выканаўцы" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Альбомы" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Спісы прайгравання" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Капіяваць" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Гучнасць %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "Паказаць" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Невядомы альбом" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Невядомы выканаўца" #~ msgid "Play music" #~ msgstr "Прайграванне музыкі" # аўтар праграмы, Nanling Zheng #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "" #~ "Перацягванне з праграмы «Файлы» і паказ размяшчэння музыкі з яе дапамогай." #~ msgid "Prefer dark theme" #~ msgstr "Аддаваць перавагу цёмнай тэме" #~ msgid "Close" #~ msgstr "Закрыць" #, c-format #~ msgid "No playlist found in: %s" #~ msgstr "Спісы прайгравання не знойдзены ў: «%s»" #~ msgid "G4Music" #~ msgstr "G4Music" ================================================ FILE: po/bg.po ================================================ # Bulgarian translation of g4music po-file. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # twlvnn kraftwerk , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-30 13:45+0000\n" "PO-Revision-Date: 2024-12-25 18:49+0100\n" "Last-Translator: twlvnn kraftwerk \n" "Language-Team: Bulgarian \n" "Language: bg\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: Gtranslator 47.0\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Програма за музика" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Елегантно си слушайте музика" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "музика;музикално устройство;медия;звук;аудио;списък с " "песни;music;sound;player;media;audio;playlist;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "„Gapless“ (известен още като: G4Music) е лек музикален плеър за GTK4, който " "се фокусира върху големи музикални колекции." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Функции" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Поддържа повечето видове музикални файлове, Samba и всякакви други " "отдалечени протоколи (зависи от GIO и GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Бързо зареждане и анализ на хиляди музикални файлове за секунди и наблюдение " "на локалните промени." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Ниски изисквания за памет за голяма музикална колекция с обложки на албуми " "(вградени и външни), без кеширане на миниатюри." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Групиране и подреждане по албум/изпълнител/заглавие, списък за разбъркване, " "пълнотекстово търсене." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Адаптивен потребителски интерфейс за различни екрани (настолен компютър, " "таблет, мобилен телефон)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Гаусово замъглена обложка на песните като за фон, поддържа светъл/тъмен " "режим на стил за GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Поддържа създаване и редактиране на списъци за изпълняване и плъзгане на " "корицата, за да промените реда или да добавите към друг списък." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Поддържа изтегляне и пускане с други програми." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Поддържа визуализатор за аудио пиковете." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Поддържа изпълняване без интервали между песните." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Поддържа нормализиране на силата на звука чрез изравняване на звука." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Поддържа посочения аудио приемник." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Поддържа контрол с MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Основен прозорец" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Изглед на албуми" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Изглед на изпълняване" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Изглед на списъка" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Общи" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Показване на клавишните комбинации" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Настройки" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Спиране на програмата" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Показване на етикетите" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Изпълнение" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Изпълнение/Пауза" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Изпълняване на следващата" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Изпълняване на предишната" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Списък с песни" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Търсене" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Превключване на подреждането" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Презареждане на фонотеката" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Запазване на списъка" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:632 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Назад" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Общи" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Режим на замъгляване на фона" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Компактен изглед на списъка" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Мрежов изглед за изпълнители/албуми" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Единично натискане за включване на елемент" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "Продължаване на изпълняването след затваряне" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Фонотека" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Зареждане на музика от папка" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Наблюдаване за промени в локалните файлове" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Зареждане на миниатюри за нелокални файлове" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Може да причини забавяне и високо използване на мрежата" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Изпълнение" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Показване на пиковото ниво на звука" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Показване на знаците" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Обръщане на обложката на албума" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Включване на изпълняване без интервали" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Нормализиране на силата на звука с регулиране" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Предпочитане на GStreamer аудио приемник" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Основно меню" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Търсене" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Подреждане по" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_Цветова схема" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_Системен стил" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Светла" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Тъмна" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_Презареждане на фонотеката" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "_Множествен избор" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_Запазване на списъка" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Настройки" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_Клавишни комбинации" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_Относно" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Албум" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Автор" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Изпълнител/Албум" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Заглавие" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Скорошни" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Разбъркване" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Файлове с изображения" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Обложката е успешно изнесена" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Обложката не може да бъде изнесена" #: src/application.vala:346 msgid "Save playlist failed" msgstr "Списъкът не може да бъде запазен" #: src/application.vala:470 msgid "Playlist Files" msgstr "Файлове на списъка" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "Списъкът е успешно запазен" #: src/application.vala:620 msgid "Keep playing" msgstr "Продължаване" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Бърза, адаптивна и лека програма за музика, написана на GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Twlvnn Kraftwerk <
kraft_werk@tutanota.com>\n" "\n" "\n" "Проектът за превод на GNOME има нужда от подкрепа.\n" "Научете повече за нас на уеб сайта ни.\n" "Докладвайте за грешки в превода в съответния раздел." #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "Не" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "Да" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "Списъкът е променен, искате ли да го запазите?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:405 src/ui/music-list.vala:421 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Изпълняване при _следваща" #: src/ui/music-list.vala:407 src/ui/music-list.vala:423 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Добавяне в _опашката" #: src/ui/music-list.vala:408 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Добавяне в _списък…" #: src/ui/music-list.vala:409 src/ui/music-list.vala:427 msgid "_Remove" msgstr "_Премахване" #: src/ui/music-list.vala:428 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Преместване в кошчето" #: src/ui/music-list.vala:636 msgid "Select All" msgstr "Избор на всички" #: src/ui/music-list.vala:653 msgid "Play at Next" msgstr "Изпълняване при следваща" #: src/ui/music-list.vala:660 msgid "Add to Queue" msgstr "Добавяне в опашката" #: src/ui/music-list.vala:667 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Добавяне в списък" #: src/ui/music-list.vala:674 msgid "Remove" msgstr "Премахване" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Изпълнение" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Случайна песен" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Показване на _файла на списъка" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Търсене на заглавие" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Търсене на албум" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Търсене на изпълнител" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Показване на _файла на обложката" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Изнасяне на обложката" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Показване на _етикетите…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Показване на музикалния _файл" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Повторение" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Предишна" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Изпълнение/Пауза" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Следваща" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Изтеглете и пуснете музикални файлове тук,\n" "или променете местоположението на музиката: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Нов списък" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:703 #, c-format msgid "No playlist found in %s" msgstr "Няма намерен списък за песни в %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Никога" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Винаги" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Само обложка" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Песен" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "В момента слушате" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Автори" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Албуми" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Списъци за изпълнение" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Копиране" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Сила на звука %d%%" #: src/ui/window.vala:119 msgid "Show" msgstr "Показване" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Неизвестен албум" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Неизвестен автор" ================================================ FILE: po/cs.po ================================================ # Czech translation for g4music. # Copyright (C) 2024 Nanling # This file is distributed under the same license as the g4music package. # Peri123214 , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-09-30 04:20+0000\n" "PO-Revision-Date: 2024-09-30 13:20+0200\n" "Last-Translator: Daniel Rusek \n" "Language-Team: Czech \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: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:234 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Přehrávač hudby" #: data/app.desktop.in:5 msgid "Play music" msgstr "Přehrát hudbu" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "hudba;zvuk;přehrávač;média;audio;seznam skladeb;" #: data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Přehrávejte hudbu elegantně" #: data/app.metainfo.xml.in:9 msgid "Nanling" msgstr "Nanling" #: data/app.metainfo.xml.in:22 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (také známý jako G4Music) je lehký hudební přehrávač napsaný v GTK4, " "zaměřuje se na velkou hudební sbírku." #: data/app.metainfo.xml.in:23 msgid "Features" msgstr "Funkce" #: data/app.metainfo.xml.in:25 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Podporuje většinu typů hudebních souborů, Sambu a jakékoli další vzdálené " "protokoly (závisí na GIO a GStreamer)." #: data/app.metainfo.xml.in:26 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Rychlé načítání a analýza tisíců hudebních souborů během několika sekund, " "sledování místních změn." #: data/app.metainfo.xml.in:27 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Nízké využití paměti při velké hudební sbírce s obaly alb (vloženými i " "externími), žádná mezipaměť pro miniatury k ukládání." #: data/app.metainfo.xml.in:28 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Seskupení a řazení podle alba / umělce / názvu, náhodného seznamu, " "fulltextového vyhledávání." #: data/app.metainfo.xml.in:29 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Plynule adaptivní uživatelské rozhraní pro různé obrazovky (desktop, tablet, " "telefon)." #: data/app.metainfo.xml.in:30 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Gaussovský rozmazaný přebal jako pozadí, následuje světlý / tmavý režim " "GNOME." #: data/app.metainfo.xml.in:31 msgid "Drag-drop from GNOME Files, showing music in Files." msgstr "Přetažení ze Souborů GNOME, zobrazení hudby v Souborech." #: data/app.metainfo.xml.in:32 msgid "Supports audio peaks visualizer." msgstr "Podporuje vizualizér maximálních úrovní zvuku." # Myslím si, že v kontextu aplikace toto bude lepší překlad než ten doslovný #: data/app.metainfo.xml.in:33 msgid "Supports gapless playback." msgstr "Podporuje automatické opakování." #: data/app.metainfo.xml.in:34 msgid "Supports normalizing volume with ReplayGain." msgstr "Podporuje normalizaci hlasitosti s ReplayGain." #: data/app.metainfo.xml.in:35 msgid "Supports PipeWire and other audio sink." msgstr "Podporuje PipeWire a další vstupní body zvuku." #: data/app.metainfo.xml.in:36 msgid "Supports MPRIS control." msgstr "Podporuje ovládání MPRIS." #: data/app.metainfo.xml.in:47 msgid "Main window" msgstr "Hlavní okno" #: data/app.metainfo.xml.in:51 msgid "Albums view" msgstr "Zobrazení alb" #: data/app.metainfo.xml.in:55 msgid "Playing view" msgstr "Zobrazení přehrávání" #: data/app.metainfo.xml.in:59 msgid "Playlist view" msgstr "Zobrazení seznamu skladeb" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Obecné" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Zobrazit zkratky" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Předvolby" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Ukončit" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Přehrávání" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Přehrát / pozastavit" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Přehrát další" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Přehrát předchozí" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Seznam skladeb" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Hledat" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Přepnout řazení" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Obnovit knihovnu" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:606 #: src/ui/store-panel.vala:413 msgid "Back" msgstr "Zpět" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Obecné" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Režim rozostření pozadí" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompaktní zobrazení seznamu skladeb" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Mřížkové zobrazení umělců / alb" #: src/gtk/preferences.ui:44 src/application.vala:403 msgid "Keep playing after window closed" msgstr "Nechat přehrávat po zavření okna" #: src/gtk/preferences.ui:57 msgid "Library" msgstr "Knihovna" #: src/gtk/preferences.ui:61 msgid "Load music from folder" msgstr "Načíst hudbu ze složky" #: src/gtk/preferences.ui:73 msgid "Monitor local file changes" msgstr "Kontrolovat místní změny souborů" #: src/gtk/preferences.ui:85 msgid "Load thumbnails for non-local files" msgstr "Načítat přebaly vzdálených souborů" #: src/gtk/preferences.ui:86 msgid "May cause slowdowns and excess network usage" msgstr "Může způsobit zpomalení a přebytečné použití sítě" #: src/gtk/preferences.ui:99 msgid "Playback" msgstr "Přehrávání" #: src/gtk/preferences.ui:103 msgid "Show audio peak level" msgstr "Zobrazovat maximální úrovně zvuku" # Myslím si, že v kontextu aplikace toto bude lepší překlad než ten doslovný #: src/gtk/preferences.ui:107 msgid "Display characters" msgstr "Zobrazováno pomocí" #: src/gtk/preferences.ui:122 msgid "Rotate album cover" msgstr "Otáčet přebal alba" # Myslím si, že v kontextu aplikace toto bude lepší překlad než ten doslovný #: src/gtk/preferences.ui:134 msgid "Enable gapless playback" msgstr "Povolit automatické opakování" #: src/gtk/preferences.ui:146 msgid "Normalize volume with ReplayGain" msgstr "Normalizovat hlasitost s ReplayGain" #: src/gtk/preferences.ui:152 msgid "Prefer audio sink of GStreamer" msgstr "Preferovaný vstupní bod GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Hlavní nabídka" #: src/gtk/store-panel.ui:29 src/ui/dialogs.vala:75 msgid "Search" msgstr "Hledat" #: src/gtk/store-panel.ui:35 msgid "Sort By" msgstr "Seřadit podle" #: src/gtk/store-panel.ui:58 msgid "_Color Scheme" msgstr "Barevné s_chéma" #: src/gtk/store-panel.ui:60 msgid "_System" msgstr "S_ystémové" #: src/gtk/store-panel.ui:65 msgid "_Light" msgstr "_Světlé" #: src/gtk/store-panel.ui:70 msgid "_Dark" msgstr "_Tmavé" #: src/gtk/store-panel.ui:77 msgid "_Reload Library" msgstr "O_bnovit knihovnu" #: src/gtk/store-panel.ui:81 msgid "_Select" msgstr "_Vybrat" #: src/gtk/store-panel.ui:87 msgid "_Preferences" msgstr "_Předvolby" #: src/gtk/store-panel.ui:91 msgid "_Keyboard Shortcuts" msgstr "_Klávesové zkratky" #: src/gtk/store-panel.ui:95 msgid "_About" msgstr "O _aplikaci" #: src/gtk/store-panel.ui:104 src/ui/preferences.vala:63 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:109 msgid "Artist" msgstr "Umělec" #: src/gtk/store-panel.ui:114 msgid "Artist/Album" msgstr "Umělec / album" #: src/gtk/store-panel.ui:119 msgid "Title" msgstr "Název" #: src/gtk/store-panel.ui:124 msgid "Recent" msgstr "Nedávné" #: src/gtk/store-panel.ui:129 msgid "Shuffle" msgstr "Náhodné" #: src/action-handles.vala:97 msgid "Image Files" msgstr "Soubory obrázků" #: src/action-handles.vala:104 msgid "Export cover successfully" msgstr "Přebal byl úspěšně exportován" #: src/action-handles.vala:104 msgid "Export cover failed" msgstr "Exportování přebalu se nezdařilo" #: src/action-handles.vala:196 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Rychlý, plynulý a lehký hudební přehrávač napsaný v GTK4." # Pozn.: V šabloně pro překlad chybí některé klíče (např. pro tlačítko smazání ve vyhledávání) #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:198 msgid "translator-credits" msgstr "Peri123214 " #: src/application.vala:307 msgid "Save playlist failed" msgstr "Uložení seznamu skladeb se nezdařilo" #: src/application.vala:425 msgid "Playlist Files" msgstr "Soubory seznamu skladeb" #: src/application.vala:439 msgid "Save playlist successfully" msgstr "Seznam skladeb byl úspěšně uložen" #: src/application.vala:618 msgid "Keep playing" msgstr "Nechat přehrávat" #: src/ui/dialogs.vala:50 src/ui/music-list.vala:641 msgid "Add to Playlist" msgstr "Přidat do seznamu skladeb" #: src/ui/dialogs.vala:55 msgid "New Playlist" msgstr "Nový seznam skladeb" #: src/ui/dialogs.vala:62 msgid "Close" msgstr "Zavřít" #: src/ui/dialogs.vala:127 src/ui/store-panel.vala:662 #, c-format msgid "No playlist found in %s" msgstr "V %s nebyl nalezen žádný seznam skladeb" #: src/ui/dialogs.vala:253 msgid "Copy" msgstr "Kopírovat" #: src/ui/dialogs.vala:344 src/ui/dialogs.vala:356 msgid "No" msgstr "Ne" #: src/ui/dialogs.vala:345 src/ui/dialogs.vala:356 msgid "Yes" msgstr "Ano" #: src/ui/music-list.vala:262 msgid "Playlist is modified, save it?" msgstr "Seznam skladeb je upraven, uložit jej?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:390 src/ui/music-list.vala:407 #: src/ui/music-list.vala:627 src/ui/music-widgets.vala:219 #: src/ui/music-widgets.vala:230 msgid "Play at Next" msgstr "Přehrát jako další" #: src/ui/music-list.vala:392 src/ui/music-list.vala:405 #: src/ui/music-list.vala:634 src/ui/music-widgets.vala:220 #: src/ui/music-widgets.vala:231 msgid "Add to Queue" msgstr "Přidat do fronty" #: src/ui/music-list.vala:393 src/ui/music-widgets.vala:221 #: src/ui/music-widgets.vala:232 src/ui/music-widgets.vala:245 msgid "Add to Playlist…" msgstr "Přidat do seznamu skladeb…" #: src/ui/music-list.vala:394 src/ui/music-list.vala:648 msgid "Remove" msgstr "Odstranit" #: src/ui/music-list.vala:410 src/ui/play-panel.vala:145 msgid "Show _Cover File" msgstr "Zob_razit přebal alba" #: src/ui/music-list.vala:412 src/ui/play-panel.vala:147 msgid "_Export Cover" msgstr "_Exportovat přebal" #: src/ui/music-list.vala:610 msgid "Select All" msgstr "Vybrat vše" #: src/ui/music-widgets.vala:223 msgid "Show List File" msgstr "Zobrazit seznam" #: src/ui/music-widgets.vala:238 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Vyhledat název" #: src/ui/music-widgets.vala:239 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Vyhledat album" #: src/ui/music-widgets.vala:240 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Vyhledat umělce" #: src/ui/music-widgets.vala:242 msgid "Show _Tags…" msgstr "Zobrazit š_títky…" #: src/ui/music-widgets.vala:243 msgid "_Show Music File" msgstr "_Zobrazit hudební soubor" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Opakovat jednu skladbu" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Přehrát předchozí" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Přehrát / pozastavit" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Přehrát další" #: src/ui/play-panel.vala:264 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Táhněte a pusťte sem soubory s hudbou,\n" "nebo změňte umístění hudby: " #: src/ui/preferences.vala:42 src/ui/preferences.vala:63 msgid "Never" msgstr "Nikdy" #: src/ui/preferences.vala:42 msgid "Always" msgstr "Vždy" #: src/ui/preferences.vala:42 msgid "Art Only" msgstr "Pouze u skladeb s přebalem" #: src/ui/preferences.vala:63 msgid "Track" msgstr "Skladba" #: src/ui/store-panel.vala:82 msgid "Playing" msgstr "Přehrává se" #: src/ui/store-panel.vala:86 msgid "Artists" msgstr "Umělci" #: src/ui/store-panel.vala:90 msgid "Albums" msgstr "Alba" #: src/ui/store-panel.vala:94 msgid "Playlists" msgstr "Seznamy skladeb" #: src/ui/store-panel.vala:421 msgid "Play" msgstr "Přehrát" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Hlasitost %d%%" #: src/ui/window.vala:110 msgid "Show" msgstr "Zobrazit" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Neznámé album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Neznámý umělec" ================================================ FILE: po/da.po ================================================ # Danish translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Alan Mortensen , 2023-25. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2025-01-21 14:09+0000\n" "PO-Revision-Date: 2025-02-18 15:06+0100\n" "Last-Translator: Alan Mortensen \n" "Language-Team: Danish \n" "Language: da\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.4.2\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "Uden pauser" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Musikafspiller" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Afspil din musik på en elegant måde" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musik;lyd;afspiller;medie;afspilningsliste;playliste;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (også kendt som G4Music) er en letvægtsmusikafspiller skrevet i " "GTK4, der fokuserer på store musiksamlinger." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Funktioner" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Understøtter de fleste musikfiltyper, Samba og andre fjernprotokoller " "(afhænger af GIO og GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Hurtig indlæsning og analyse af tusindvis af musikfiler på meget få sekunder " "plus overvågning af lokale ændringer." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Lavt hukommelsesforbrug til store musiksamlinger med albumcovers (indlejrede " "og eksterne) og gemmer ingen miniaturecache." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Gruppering og sortering efter album/kunstner/titel, blande-liste, " "fuldtekstsøgning." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Flydende tilpasning af brugergrænseflade til forskellige skærme (desktop, " "tablet, mobil)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "Gaussisk sløret cover som baggrund, følger GNOMEs lyse/mørke tilstand." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Understøtter oprettelse og redigering af afspilningslister, trækning af " "cover for at ændre rækkefølge eller tilføjelse til en anden afspilningsliste." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Understøtter træk-og-slip med andre programmer." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Understøtter visualisering af lydtoppe." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Understøtter afspilning uden pauser." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Understøtter normalisering af lydstyrke med ReplayGain" #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Understøtter angivet lyddatamodtager." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Understøtter MPRIS-kontrol." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Hovedvindue" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albumvisning" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Afspilningsvisning" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Afspilningslistevisning" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Generelt" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Vis genveje" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Indstillinger" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Afslut" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Vis mærkater" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Afspilning" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Afspil/pause" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Afspil næste" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Afspil forrige" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Afspilningsliste" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Søgning" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Skift sortering" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Genindlæs bibliotek" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Gem liste" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:632 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Tilbage" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Generelt" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Baggrundssløringstilstand" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompakt visning af afspilningsliste" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Gittervisning af kunstnere/album" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Enkeltklik for at aktivere element" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "Fortsæt med at afspille, efter vinduet er lukket" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotek" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Indlæs musik fra mappe" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Overvåg lokale filændringer" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Indlæs ikkelokale filers miniaturebilleder" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Kan forårsage langsommere afvikling og forøget netværksforbrug" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Afspilning" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Vis topniveau for lyd" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Vis tegn" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Rotér albumcover" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Aktivér afspilning uden mellemrum" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalisér lydstyrke med ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Foretræk GSteamers lyddatamodtager" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Hovedmenu" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Søg" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Sortér efter" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_Farveskema" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_System" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Lys" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Mørk" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_Genindlæs bibliotek" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "_Vælg flere" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "G_em liste" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Indstillinger" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_Tastaturgenveje" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_Om" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Kunstner" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Kunstner/Album" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Titel" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Seneste" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Bland" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Billedfiler" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Eksport af cover gennemført" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Eksport af cover mislykkedes" #: src/application.vala:346 msgid "Save playlist failed" msgstr "Afspilningsliste kunne ikke gemmes" #: src/application.vala:470 msgid "Playlist Files" msgstr "Afspilningslistefiler" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "Afspilningsliste blev gemt" #: src/application.vala:620 msgid "Keep playing" msgstr "Fortsæt med at afspille" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "En hurtig og flydende letvægtsmusikafspiller skrevet i GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Alan Mortensen \n" "\n" "Dansk-gruppen\n" "Websted http://dansk-gruppen.dk\n" "E-mail " #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "Nej" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "Ja" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "Vil du gemme den ændrede afspilningsliste?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:405 src/ui/music-list.vala:421 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Afspil ved _næste" #: src/ui/music-list.vala:407 src/ui/music-list.vala:423 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "_Føj til afspilningskø" #: src/ui/music-list.vala:408 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "F_øj til afspilningsliste …" #: src/ui/music-list.vala:409 src/ui/music-list.vala:427 msgid "_Remove" msgstr "F_jern" #: src/ui/music-list.vala:428 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Fl_yt til papirkurven" #: src/ui/music-list.vala:636 msgid "Select All" msgstr "Markér alt" #: src/ui/music-list.vala:653 msgid "Play at Next" msgstr "Afspil ved næste" #: src/ui/music-list.vala:660 msgid "Add to Queue" msgstr "Føj til afspilningskø" #: src/ui/music-list.vala:667 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Føj til afspilningsliste" #: src/ui/music-list.vala:674 msgid "Remove" msgstr "Fjern" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Afspil" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Tilfældig afspilning" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "_Vis listefil" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Søg efter titel" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Søg efter album" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Søg efter kunstner" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Vis _coverfil" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Eksportér cover" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Vis _mærkater …" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Vis musikfil" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Enkelt afspilning" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Afspil forrige" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Afspil/pause" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Afspil næste" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Træk og slip musikfiler her,\n" "eller ændr musikplacering: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Ny afspilningsliste" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:703 #, c-format msgid "No playlist found in %s" msgstr "Fandt ingen afspilningsliste i %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Aldrig" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Altid" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Kun kunst" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Spor" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Afspiller" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Kunstnere" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albummer" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Afspilningslister" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopiér" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Lydstyrke %d %%" #: src/ui/window.vala:119 msgid "Show" msgstr "Vis" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Ukendt album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Ukendt kunstner" #~ msgid "Play music" #~ msgstr "Afspil musik" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "Træk-og-slip fra GNOME Filer og få vist musik i Filer." #~ msgid "Prefer dark theme" #~ msgstr "Foretræk mørkt tema" #~ msgid "Close" #~ msgstr "Luk" #~ msgid "G4Music" #~ msgstr "G4Musik" ================================================ FILE: po/de.po ================================================ # GERMAN TRANSLATION FOR G4MUSIC. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Jürgen Benvenuti , 2022-2024. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-14 16:49+0000\n" "PO-Revision-Date: 2024-10-28 10:35+0100\n" "Last-Translator: Jürgen Benvenuti \n" "Language-Team: German \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: Poedit 3.4.4\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:271 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Musikwiedergabe" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Geben Sie Ihre Musik auf elegante Weise wieder" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;Musik;Klang;Wiedergabe;Medien;Audio;" "Wiedergabeliste;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (alias »G4Music«) ist eine leichtgewichtige Musikwiedergabe, " "geschrieben in GTK, die sich auf große Musiksammlungen konzentriert." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Funktionen" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Unterstützt die meisten Musikdateitypen, Samba und alle anderen " "Fernzugriffsprotokolle (abhängig von GIO und GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Schnelles Laden und Verarbeiten von Tausenden von Musikdateien in wenigen " "Sekunden, Überwachen von lokalen Änderungen." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Geringer Speicherbedarf für große Musiksammlungen mit Albumbildern " "(eingebettet und extern), kein Zwischenspeichern von Vorschaubildern." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Gruppieren und sortieren nach Album/Interpret/Titel, Liste durchmischen, " "Volltextsuche." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Geschmeidige adaptive Benutzeroberfläche für unterschiedliche Bildschirme " "(Desktop, Tablet, mobiles Gerät)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Gauß-weichgezeichnete Albumbilder als Hintergrund, folgt dem hellen bzw. " "dunklen GNOME-Modus." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Unterstützt das Erstellen und Bearbeiten von Wiedergabelisten; ziehen Sie " "das Albumbild, um die Reihenfolge zu ändern oder zu einer anderen " "Wiedergabeliste hinzuzufügen." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Unterstützt Ziehen und Ablegen mit anderen Anwendungen." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Unterstützt die Visualisierung von Audiospitzenpegeln." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Unterstützt lückenlose Wiedergabe." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Unterstützt die Normalisierung der Lautstärke durch ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Unterstützt angegebenen Audio-Ausgang." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Unterstützt MPRIS-Steuerung." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Hauptfenster" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albenansicht" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Wiedergabeansicht" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Wiedergabelistenansicht" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Allgemein" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Tastenkürzel anzeigen" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Einstellungen" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Beenden" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Schlagwörter anzeigen" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Wiedergabe" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Wiedergabe/Pause" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Den nächsten Titel wiedergeben" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Den vorherigen Titel wiedergeben" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Wiedergabeliste" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Suchen" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Sortiermodus umschalten" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Bibliothek neu laden" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Liste speichern" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:420 msgid "Back" msgstr "Zurück" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Allgemein" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Hintergrund weichzeichnen" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompaktansicht der Wiedergabeliste" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Rasteransicht für Interpreten/Alben" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Einfacher Klick zum Aktivieren des Elements" #: src/gtk/preferences.ui:56 src/application.vala:419 msgid "Keep playing after window closed" msgstr "Musik weiterspielen nach dem Schließen des Fensters" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliothek" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Musik aus Ordner laden" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Lokale Dateiänderungen überwachen" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Vorschaubilder für nicht lokale Dateien laden" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Kann zu Verlangsamungen und übermäßiger Netznutzung führen" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Wiedergabe" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Audiospitzenpegel anzeigen" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Zeichen anzeigen" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Sich drehendes Albumbild" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Lückenlose Wiedergabe aktivieren" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "ReplayGain zur Normalisierung der Lautstärke verwenden" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Bevorzugter GStreamer-Audio-Ausgang" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Hauptmenü" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Suchen" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Sortieren nach" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Farbschema" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_System" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Hell" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Dunkel" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "Bibliothek neu _laden" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Mehrere auswählen" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "Liste _speichern" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Einstellungen" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Tastenkürzel" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Info" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Interpret" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Interpret/Album" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Titel" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Zuletzt gespielt" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Durchmischen" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Bilddateien" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Exportieren des Albumbilds erfolgreich" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Exportieren des Albumbilds fehlgeschlagen" #: src/application.vala:344 msgid "Save playlist failed" msgstr "Speichern der Wiedergabeliste fehlgeschlagen" #: src/application.vala:441 msgid "Playlist Files" msgstr "Wiedergabelistendateien" #: src/application.vala:455 msgid "Save playlist successfully" msgstr "Speichern der Wiedergabeliste erfolgreich" #: src/application.vala:591 msgid "Keep playing" msgstr "Musik weiterspielen" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "" "Eine schnelle, geschmeidige, leichtgewichtige Musikwiedergabe, geschrieben " "in GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Jürgen Benvenuti , 2022-2024" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Nein" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Ja" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Wiedergabeliste wurde geändert, soll sie gespeichert werden?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Als _Nächstes wiedergeben" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Zur _Warteschlange hinzufügen" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Zur Wieder_gabeliste hinzufügen …" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Entfernen" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "In den _Papierkorb verschieben" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Alle auswählen" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Als Nächstes wiedergeben" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Zur Warteschlange hinzufügen" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Zur Wiedergabeliste hinzufügen" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Entfernen" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:428 msgid "Play" msgstr "Wiedergeben" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Zufällige Wiedergabe" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Listen_datei anzeigen" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:63 msgid "Search Title" msgstr "Titel suchen" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:61 msgid "Search Album" msgstr "Album suchen" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:62 msgid "Search Artist" msgstr "Interpret suchen" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Albumbild-_Datei anzeigen" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "Albumbild expo_rtieren" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "_Schlagwörter anzeigen …" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Musik_datei anzeigen" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Einfachschleife" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Den vorherigen Titel wiedergeben" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Wiedergabe/Pause" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Den nächsten Titel wiedergeben" #: src/ui/play-panel.vala:258 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Musikdateien hierher ziehen und ablegen,\n" "oder den Musikspeicherort ändern: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Neue Wiedergabeliste" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:697 #, c-format msgid "No playlist found in %s" msgstr "Keine Wiedergabeliste gefunden in %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nie" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Immer" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Nur mit Albumbild" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Titel" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Aktuelle Wiedergabe" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Interpreten" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Alben" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Wiedergabelisten" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopieren" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Lautstärke %d%%" #: src/ui/window.vala:119 msgid "Show" msgstr "Anzeigen" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Unbekanntes Album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Unbekannter Interpret" #~ msgid "Play music" #~ msgstr "Musik wiedergeben" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Close" #~ msgstr "Schließen" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "" #~ "Ziehen und ablegen aus GNOME-»Dateien«, zeigt Musik in »Dateien« an." #~ msgid "Prefer dark theme" #~ msgstr "Dunkles Thema verwenden" #~ msgid "_Add to Playlist…" #~ msgstr "Zur Wiedergabeliste _hinzufügen …" #~ msgid "G4Music" #~ msgstr "G4Music" #~ msgid "Repeat Music" #~ msgstr "Den Titel wiederholen" #~ msgid "Music Location" #~ msgstr "Musikspeicherort" #~ msgid "Loading…" #~ msgstr "Wird geladen …" #~ msgid "By Album" #~ msgstr "Nach Album" #~ msgid "By Date" #~ msgstr "Nach Datum" #~ msgid "Appearance" #~ msgstr "Erscheinungsbild" #~ msgid "Experimental" #~ msgstr "Experimentell" ================================================ FILE: po/el.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2025-09-11 07:33+0000\n" "PO-Revision-Date: 2025-09-13 00:09+0300\n" "Last-Translator: Dimitris Spentzos \n" "Language-Team: \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.7\n" #: data/app.desktop.in:2 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:3 msgid "Music Player" msgstr "Αναπαραγωγή μουσικής" #: data/app.desktop.in:4 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Ακούστε τη μουσική σας με κομψότητα" #: data/app.desktop.in:11 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;μουσική;λίστα;αναπαραγωγή;τραγούδια;ήχος;πολυμέσα;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Το Gapless (γνωστό κι ως «G4Music») είναι μια λιτή εφαρμογή αναπαραγωγής " "μουσικής, γραμμένη σε GTK4, που εστιάζει σε μεγάλες συλλογές μουσικής." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Δυνατότητες" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Υποστηρίζει τους περισσότερους τύπους αρχείων μουσικής, το Samba και " "οποιοδήποτε άλλο απομακρυσμένο πρωτόκολλο (εξαρτάται από τα GIO και " "GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Γρήγορη φόρτωση και ανάλυση χιλιάδων αρχείων μουσικής σε πολύ λίγα " "δευτερόλεπτα, εποπτεία για τοπικές αλλαγές." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Χαμηλή χρήση μνήμης για μεγάλες συλλογές μουσικής με εξώφυλλα άλμπουμ " "(ενσωματωμένα και εξωτερικά), χωρίς αποθήκευση προσωρινών μικρογραφιών." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Ομαδοποίηση και ταξινόμηση κατά άλμπουμ/καλλιτέχνη/τίτλο, τυχαία " "αναπαραγωγή λίστας, αναζήτηση πλήρους κειμένου." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Ευέλικτο και προσαρμοστικό γραφικό περιβάλλον για διαφορετικές οθόνες " "(υπολογιστές, tablet, κινητές συσκευές)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Γκαουσιανό θόλωμα εξωφύλλου ως εφέ παρασκηνίου, προσαρμογή στην " "ανοιχτόχρωμη/σκουρόχρωμη λειτουργία του GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Υποστηρίζει δημιουργία και επεξεργασία λιστών αναπαραγωγής, μεταφορά " "εξώφυλλων για αλλαγή της σειράς ή προσθήκη σε άλλη λίστα αναπαραγωγής." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Υποστηρίζει μεταφορά και απόθεση με άλλες εφαρμογές." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Υποστηρίζει οπτικοποίηση κορυφώσεων ήχου." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Υποστηρίζει απρόσκοπτη αναπαραγωγή." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Υποστηρίζει κανονικοποίηση έντασης με το ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Υποστηρίζει ορισμό συγκεκριμένης διεπαφής ήχου." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Υποστηρίζει έλεγχο MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Κύριο παράθυρο" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Προβολή άλμπουμ" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Προβολή αναπαραγωγής" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Προβολή λιστών αναπαραγωγής" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Γενικά" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Εμφάνιση συντομεύσεων" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Προτιμήσεις" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Έξοδος" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Εμφάνιση ετικετών" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Αναπαραγωγή" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Αναπαραγωγή/παύση" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Αναπαραγωγή επόμενου" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Αναπαραγωγή προηγούμενου" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Λίστα αναπαραγωγής" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Αναζήτηση" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Εναλλαγή ταξινόμησης" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Επαναφόρτωση βιβλιοθήκης" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Αποθήκευση λίστας" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:626 #: src/ui/store-panel.vala:420 msgid "Back" msgstr "Πϊσω" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Γενικά" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Λειτουργία θολώματος φόντου" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Συμπαγής προβολή λίστας αναπαραγωγής" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Προβολή πλέγματος για καλλιτέχνες/άλμπουμ" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Ενεργοποίηση στοιχείων με ένα κλικ" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "Συνέχεια αναπαραγωγής μετά το κλείσιμο του παραθύρου" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Βιβλιοθήκη" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Φόρτωση μουσικής από φάκελο" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Εποπτεία για αλλαγές στα τοπικά αρχεία" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Φόρτωση μικρογραφιών για μη τοπικά αρχεία" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Ενδέχεται να προκαλέσει καθυστερήσεις και υπερβολική χρήση δικτύου" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Αναπαραγωγή" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Εμφάνιση επιπέδου κορύφωσης ήχου" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Εμφανιζόμενοι χαρακτήρες" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Περιστροφή εξώφυλλου άλμπουμ" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Ενεργοποίηση απρόσκοπτης αναπαραγωγής" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Κανονικοποίηση έντασης με το ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Προτιμώμενη διεπαφή ήχου για το GStreamer" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Κύριο μενού" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Αναζήτηση" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Ταξινόμηση κατά" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_Σύνολο χρωμάτων" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_Θέμα συστήματος" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Ανοιχτόχρωμο" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "Σ_κουρόχρωμο" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_Επαναφόρτωση βιβλιοθήκης" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "_Πολλαπλή επιλογή" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_Αποθήκευση λίστας" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Προτιμήσεις" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "Συντομεύσεις π_ληκτρολογίου" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "Πλ_ηροφορίες" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Άλμπουμ" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Καλλιτέχνης" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Καλλιτέχνης/άλμπουμ" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Τίτλος" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Πρόσφατα" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Τυχαία αναπαραγωγή" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Αρχεία εικόνων" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Επιτυχής εξαγωγή εξώφυλλου" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Αποτυχία εξαγωγής εξώφυλλου" #: src/application.vala:346 msgid "Save playlist failed" msgstr "Αποτυχία αποθήκευσης λίστας αναπαραγωγής" #: src/application.vala:470 msgid "Playlist Files" msgstr "Αρχεία λιστών αναπαραγωγής" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "Επιτυχής αποθήκευση λίστας αναπαραγωγής" #: src/application.vala:620 msgid "Keep playing" msgstr "Συνέχεια αναπαραγωγής" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:52 msgid "translator-credits" msgstr "" "Ομάδα ελληνικής μετάφρασης GNOME\n" " Δημήτριος Σπέντζος " #: src/ui/dialogs.vala:98 src/ui/dialogs.vala:110 msgid "No" msgstr "Όχι" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:110 msgid "Yes" msgstr "Ναι" #: src/ui/music-list.vala:270 msgid "Playlist is modified, save it?" msgstr "Η λίστα αναπαραγωγής τροποποιήθηκε, θέλετε να κάνετε αποθήκευση;" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:399 src/ui/music-list.vala:415 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Αναπαραγωγή στο _επόμενο" #: src/ui/music-list.vala:401 src/ui/music-list.vala:417 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Προσθήκη στην _ουρά αναπαραγωγής" #: src/ui/music-list.vala:402 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Προσθήκη σε _λίστα αναπαραγωγής…" #: src/ui/music-list.vala:403 src/ui/music-list.vala:421 msgid "_Remove" msgstr "_Αφαίρεση" #: src/ui/music-list.vala:422 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Μετακίνηση στα απορρίμματα" #: src/ui/music-list.vala:630 msgid "Select All" msgstr "Επιλογή όλων" #: src/ui/music-list.vala:647 msgid "Play at Next" msgstr "Αναπαραγωγή στο επόμενο" #: src/ui/music-list.vala:654 msgid "Add to Queue" msgstr "Προσθήκη στην ουρά αναπαραγωγής" #: src/ui/music-list.vala:661 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Προσθήκη σε λίστα αναπαραγωγής" #: src/ui/music-list.vala:668 msgid "Remove" msgstr "Αφαίρεση" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:428 msgid "Play" msgstr "Αναπαραγωγή" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Τυχαία αναπαραγωγή" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Εμφάνιση _αρχείου λίστας" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:63 msgid "Search Title" msgstr "Αναζήτηση τίτλου" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:61 msgid "Search Album" msgstr "Αναζήτηση άλμπουμ" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:62 msgid "Search Artist" msgstr "Αναζήτηση καλλιτέχνη" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Εμφάνιση _αρχείου εξώφυλλου" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Εξαγωγή εξώφυλλου" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Εμφάνιση _ετικετών…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Εμφάνιση _αρχείου μουσικής" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Επανάληψη κομματιού" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Αναπαραγωγή προηγούμενου" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Αναπαραγωγή/παύση" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Αναπαραγωγή επόμενου" #: src/ui/play-panel.vala:56 src/ui/store-panel.vala:84 msgid "Playing" msgstr "Αναπαραγωγή" #: src/ui/play-panel.vala:266 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Σύρετε και αποθέστε αρχεία μουσικής εδώ\n" "ή αλλάξτε την τοποθεσία μουσικής: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Νέα λίστα" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:712 #, c-format msgid "No playlist found in %s" msgstr "Δεν βρέθηκε λίστα αναπαραγωγής στον φάκελο «%s»" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Ποτέ" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Πάντα" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Μόνο με εξώφυλλο" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Κομμάτι" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Καλλιτέχνες" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Άλμπουμ" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Λίστες αναπαραγωγής" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Αντιγραφή" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Ένταση: %d%%" #: src/ui/window.vala:126 msgid "Show" msgstr "Εμφάνιση" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Άγνωστο άλμπουμ" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Άγνωστος καλλιτέχνης" ================================================ FILE: po/es.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # Daniel Mustieles , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/-/issues\n" "POT-Creation-Date: 2023-08-19 04:11+0000\n" "PO-Revision-Date: 2024-01-30 09:43+0100\n" "Last-Translator: Daniel Mustieles \n" "Language-Team: Spanish - Spain \n" "Language: es_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: Gtranslator 45.alpha0\n" #: data/app.desktop.in:3 src/application.vala:232 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Reproductor de Musica" #: data/app.desktop.in:5 msgid "Play music" msgstr "Reproducir musica" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musica;sonido;reproductor;medios;audio;lista de reproduccion" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "General" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Mostrar Atajos" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferencias" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Salir" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Reproduccion" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Reproducir/Pausar" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Reproducir Siguiente" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Reproducir Anterior" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Lista de Reproduccion" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Buscar" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Cambiar Orden" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Recargar Biblioteca" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:340 msgid "Back" msgstr "Atras" #: src/gtk/preferences.ui:10 msgid "General" msgstr "General" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Preferir modo oscuro" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Modo de blur de fondo" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Playlist compacta" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "Vista en cuadricula para artistas/albums" #: src/gtk/preferences.ui:56 src/application.vala:432 msgid "Keep playing after window closed" msgstr "Seguir reproduciendo despues de cerrar la ventana" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Biblioteca" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Cargar musica desde carpeta" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Monitorear cambios de archivos locales" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Cargar miniatura de archivos no-locales" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Puede causar ralentizacion y exceso de uso de red" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Reproduccion" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Mostrar pico de nivel de audio" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Mostrar Caracteres" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Rotar portada del album" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Activar reproduccion sin interrupciones" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Usar ReplayGain para normalizar el volumen" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Reproducir" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu Principal" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Ordenar Por" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Buscar" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "_Recargar Biblioteca" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Preferencias" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "_Atajos de Teclado" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "_Acerca de" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artista/Album" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Titulo" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Reciente" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Aleatorio" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Un rapido, fluido y ligero reproductor de musica escrito en GTK4" #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "Radi4ever" #: src/application.vala:613 msgid "Keep playing" msgstr "Seguir Reproduciendo" #. Translators: Play this music at next position of current playing music #: src/ui/music-widgets.vala:252 src/ui/music-widgets.vala:293 #: src/ui/music-widgets.vala:303 msgid "Play at Next" msgstr "Reproducir a la Siguiente" #: src/ui/music-widgets.vala:253 src/ui/music-widgets.vala:292 #: src/ui/music-widgets.vala:302 src/ui/store-panel.vala:345 msgid "Play" msgstr "Reproducir" #: src/ui/music-widgets.vala:256 src/ui/play-panel.vala:237 msgid "Show _Cover File" msgstr "Mostrar _Portada" #: src/ui/music-widgets.vala:258 src/ui/play-panel.vala:239 msgid "_Export Cover" msgstr "_Exportar Portada" #: src/ui/music-widgets.vala:295 msgid "Show List File" msgstr "Mostrar lista de archivos" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:56 msgid "Search Title" msgstr "Buscar Titulo" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:54 msgid "Search Album" msgstr "Buscar Album" #: src/ui/music-widgets.vala:311 src/ui/play-panel.vala:55 msgid "Search Artist" msgstr "Buscar Artista" #: src/ui/music-widgets.vala:312 msgid "_Show Music File" msgstr "_Mostrar Archivo de Pista" #. Translators: single loop the current music #: src/ui/play-bar.vala:74 msgid "Single Loop" msgstr "Repetir una vez" #: src/ui/play-bar.vala:84 msgid "Play Previous" msgstr "Reproducir Anterior" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:90 msgid "Play/Pause" msgstr "Reproducir/Pausar" #: src/ui/play-bar.vala:97 msgid "Play Next" msgstr "Reproducir Siguiente" #: src/ui/play-panel.vala:280 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Arrastra y suelta las canciones aqui,\n" "o cambia la ubicacion de la musica" #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "Nunca" #: src/ui/preferences.vala:46 msgid "Always" msgstr "Siempre" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "Solo Portada" #: src/ui/preferences.vala:67 msgid "Track" msgstr "Pista" #: src/ui/store-panel.vala:74 msgid "Playing" msgstr "Reproduciendo Ahora" #: src/ui/store-panel.vala:79 msgid "Artists" msgstr "Artistas" #: src/ui/store-panel.vala:84 msgid "Albums" msgstr "Albums" #: src/ui/store-panel.vala:374 msgid "Playlists" msgstr "Listas De Reproduccion" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volumen %d%%" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album Desconocido" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista Desconocido" #~ msgid "Repeat Music" #~ msgstr "Repetir Pista" #~ msgid "Appearance" #~ msgstr "Apariencia" #~ msgid "Music Location" #~ msgstr "Localizacion de la Musica" #~ msgid "By Date" #~ msgstr "Por Fecha" #~ msgid "Show Album" #~ msgstr "Mostrar Album" #~ msgid "Show Artist" #~ msgstr "Mostrar Artista" #~ msgid "Loading..." #~ msgstr "Cargando..." ================================================ FILE: po/et.po ================================================ # G4Music Estonian translation. # Copyright (C) 2022 # This file is distributed under the same license as the G4Music software. # Henri , 2022. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-26 21:16+0800\n" "PO-Revision-Date: 2022-08-01 18:49+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: et\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.1.1\n" #: data/app.desktop.in:3 src/application.vala:225 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Muusikamängija" #: data/app.desktop.in:5 msgid "Play music" msgstr "Kuula muusikat" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;muusika;heli;muusikamängija;meedia;" "esitusloend;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Üldine" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Kiirklahvide näitamine" #: src/gtk/help-overlay.ui:20 #, fuzzy msgctxt "shortcut window" msgid "Preferences" msgstr "_Eelistused" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Sulge" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Esitamine" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Esita/Peata" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Järgmine lugu" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Eelmine lugu" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Esitusloend" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Otsi" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "" #: src/gtk/help-overlay.ui:72 #, fuzzy msgctxt "shortcut window" msgid "Reload Library" msgstr "_Loetelu värskendamine" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:290 msgid "Back" msgstr "Tagasi" #: src/gtk/preferences.ui:10 #, fuzzy msgid "General" msgstr "Üldine" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Eelista tumedat teemat" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "" #: src/gtk/preferences.ui:44 src/application.vala:424 msgid "Keep playing after window closed" msgstr "Mängi ka siis kui aken on suletud" #: src/gtk/preferences.ui:57 msgid "Library" msgstr "" #: src/gtk/preferences.ui:61 msgid "Load music from folder" msgstr "Lae muusikat kaustast" #: src/gtk/preferences.ui:73 msgid "Monitor local file changes" msgstr "" #: src/gtk/preferences.ui:85 msgid "Load thumbnails for non-local files" msgstr "Lae pisipilt võõrate failide jaoks" #: src/gtk/preferences.ui:86 msgid "May cause slowdowns and excess network usage" msgstr "Võib põhjustada aeglustusi ja tekitada üleliigset võrgukasutust" #: src/gtk/preferences.ui:99 msgid "Playback" msgstr "Esitamine" #: src/gtk/preferences.ui:103 msgid "Show audio peak level" msgstr "Heli valjutaseme näitamine" #: src/gtk/preferences.ui:107 msgid "Display characters" msgstr "" #: src/gtk/preferences.ui:122 msgid "Rotate album cover" msgstr "" #: src/gtk/preferences.ui:134 msgid "Enable gapless playback" msgstr "Ilma auguta järjest esitamine" #: src/gtk/preferences.ui:146 msgid "Normalize volume with ReplayGain" msgstr "ReplayGain'i helitugevuse normaliseerimise kasutamine" #: src/gtk/preferences.ui:152 #, fuzzy msgid "Prefer audio sink" msgstr "Pipewire tarkvara kasutamine" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Peamine menüü" #: src/gtk/store-panel.ui:28 #, fuzzy msgid "Sort By" msgstr "Sorteerimine" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Otsi" #: src/gtk/store-panel.ui:73 #, fuzzy msgid "_Reload Library" msgstr "_Loetelu värskendamine" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Eelistused" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "_Kiirklahvid" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "_Teave" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:64 #, fuzzy msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 #, fuzzy msgid "Artist" msgstr "Esitaja" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "" #: src/gtk/store-panel.ui:111 #, fuzzy msgid "Title" msgstr "Pealkiri" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Juhuesitus" #: src/action-handles.vala:188 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Kiire, sidus ja lihtne muusikamängija GTK4 alustel." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:190 msgid "translator-credits" msgstr "Henri , 2022" #: src/application.vala:573 msgid "Keep playing" msgstr "Jätka mängimist" #: src/ui/music-widgets.vala:232 src/ui/music-widgets.vala:260 #, fuzzy msgid "Play at Next" msgstr "Järgmine lugu" #: src/ui/music-widgets.vala:233 src/ui/music-widgets.vala:259 #, fuzzy msgid "Play" msgstr "Esitamine" #: src/ui/music-widgets.vala:236 src/ui/play-panel.vala:222 #, fuzzy msgid "Show _Cover File" msgstr "_Kuva failides" #: src/ui/music-widgets.vala:238 src/ui/play-panel.vala:224 msgid "_Export Cover" msgstr "_Ekspordi loo pilt" #: src/ui/music-widgets.vala:266 src/ui/play-panel.vala:56 #, fuzzy msgid "Search Title" msgstr "Otsi" #: src/ui/music-widgets.vala:267 src/ui/play-panel.vala:54 #, fuzzy msgid "Search Album" msgstr "Kuva album" #: src/ui/music-widgets.vala:268 src/ui/play-panel.vala:55 #, fuzzy msgid "Search Artist" msgstr "Kuva esitaja" #: src/ui/music-widgets.vala:269 #, fuzzy msgid "_Show Music File" msgstr "_Kuva failides" #: src/ui/play-bar.vala:75 msgid "Repeat Music" msgstr "Loo kordamine" #: src/ui/play-bar.vala:85 msgid "Play Previous" msgstr "Eelmine lugu" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:91 msgid "Play/Pause" msgstr "Esita/Peata" #: src/ui/play-bar.vala:98 msgid "Play Next" msgstr "Järgmine lugu" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Lohista muusikafail siia,\n" "või muuda muusika asukoht: " #: src/ui/preferences.vala:44 src/ui/preferences.vala:64 msgid "Never" msgstr "" #: src/ui/preferences.vala:44 msgid "Always" msgstr "" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "" #: src/ui/preferences.vala:64 msgid "Track" msgstr "" #: src/ui/store-panel.vala:66 #, fuzzy msgid "Playing" msgstr "Esitusloend" #: src/ui/store-panel.vala:72 #, fuzzy msgid "Artists" msgstr "Esitaja" #: src/ui/store-panel.vala:77 #, fuzzy msgid "Albums" msgstr "Album" #: src/ui/volume-button.vala:32 msgid "Volume: " msgstr "" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Tundmatu album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Tundmatu esitaja" #~ msgid "Appearance" #~ msgstr "Välimus" #~ msgid "Music Location" #~ msgstr "Muusika asukoht" #~ msgid "Experimental" #~ msgstr "Eksperimentaalne" #~ msgid "By Date" #~ msgstr "Kuupäev" #~ msgid "Loading..." #~ msgstr "Laadimine…" ================================================ FILE: po/eu.po ================================================ # Basque translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Asier Sarasua Garmendia , 2023. # msgid "" msgstr "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/-/issues\n" "POT-Creation-Date: 2023-08-17 10:19+0000\n" "PO-Revision-Date: 2023-08-18 18:04+0000\n" "Last-Translator: Asier Sarasua Garmendia \n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/app.desktop.in:3 src/application.vala:232 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Musika-erreproduzitzailea" #: data/app.desktop.in:5 msgid "Play music" msgstr "Erreproduzitu musika" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musika;soinua;erreproduzigailua;multimedia;audioa;erreprodukzio-zerrenda;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Orokorra" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Erakutsi lasterbideak" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Hobespenak" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Irten" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Erreproduzitu" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Erreproduzitu/pausarazi" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Erreproduzitu hurrengoa" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Erreproduzitu aurrekoa" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Erreprodukzio-zerrenda" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Bilatu" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Txandakatu ordena" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Birkargatu liburutegia" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:340 msgid "Back" msgstr "Atzera" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Orokorra" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Hobetsi gai iluna" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Atzeko plano lausoaren modua" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Erreprodukzio-zerrendaren ikuspegi trinkoa" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "Sareta-ikuspegia artistetarako/albumetarako" #: src/gtk/preferences.ui:56 src/application.vala:432 msgid "Keep playing after window closed" msgstr "Jarraitu erreproduzitzen leihoa itxi ondoren" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Liburutegia" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Kargatu musika karpetatik" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Monitorizatu fitxategi lokalen aldaketak" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Kargatu miniaturak fitxategi ez lokaletarako" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Moteltzeak eta sarearen gehiegizko erabilera gertatu daitezke" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Erreproduzitu" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Erakutsi audioaren gailur-maila" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Bistaratu karaktereak" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Biratu albumaren azala" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Gaitu tarterik gabeko erreprodukzioa" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalizatu bolumena ReplayGain bidez" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Hobetsi GStreamer-en audio-hustubidea" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu nagusia" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Ordenatu honen arabera" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Bilatu" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "Birkargatu _liburutegia" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Hobespenak" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "Las_ter-teklak" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "Honi _buruz" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "Albuma" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artista/Albuma" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Titulua" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Azken aldikoa" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Ausaz" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "GTK4 teknologiarekin idatzitako musika-erreproduzigailu arin eta azkarra." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "translator-credits" #: src/application.vala:613 msgid "Keep playing" msgstr "Jarraitu erreproduzitzen" #. Translators: Play this music at next position of current playing music #: src/ui/music-widgets.vala:252 src/ui/music-widgets.vala:293 #: src/ui/music-widgets.vala:303 msgid "Play at Next" msgstr "Erreproduzitu hurrengoan" #: src/ui/music-widgets.vala:253 src/ui/music-widgets.vala:292 #: src/ui/music-widgets.vala:302 src/ui/store-panel.vala:345 msgid "Play" msgstr "Erreproduzitu" #: src/ui/music-widgets.vala:256 src/ui/play-panel.vala:231 msgid "Show _Cover File" msgstr "Erakutsi _azaleko fitxategia" #: src/ui/music-widgets.vala:258 src/ui/play-panel.vala:233 msgid "_Export Cover" msgstr "_Esportatu azala" #: src/ui/music-widgets.vala:295 msgid "Show List File" msgstr "Erakusti zerrenda-fitxategia" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:55 msgid "Search Title" msgstr "Bilatu titulua" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:53 msgid "Search Album" msgstr "Bilatu albuma" #: src/ui/music-widgets.vala:311 src/ui/play-panel.vala:54 msgid "Search Artist" msgstr "Bilatu artista" #: src/ui/music-widgets.vala:312 msgid "_Show Music File" msgstr "_Erakutsi musika-fitxategia" #. Translators: single loop the current music #: src/ui/play-bar.vala:76 msgid "Single Loop" msgstr "Begizta bakarra" #: src/ui/play-bar.vala:86 msgid "Play Previous" msgstr "Erreproduzitu aurrekoa" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:92 msgid "Play/Pause" msgstr "Erreproduzitu/pausarazi" #: src/ui/play-bar.vala:99 msgid "Play Next" msgstr "Erreproduzitu hurrengoa" #: src/ui/play-panel.vala:274 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "Arrastatu eta jaregin musika-fitxategiak hemen,\n" "edo aldatu musikaren kokalekua: " #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "Inoiz ere ez" #: src/ui/preferences.vala:46 msgid "Always" msgstr "Beti" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "Artea soilik" #: src/ui/preferences.vala:67 msgid "Track" msgstr "Pista" #: src/ui/store-panel.vala:74 msgid "Playing" msgstr "Erreproduzitzen" #: src/ui/store-panel.vala:79 msgid "Artists" msgstr "Artistak" #: src/ui/store-panel.vala:84 msgid "Albums" msgstr "Albumak" #: src/ui/store-panel.vala:374 msgid "Playlists" msgstr "Erreprodukzio-zerrendak" #. Translators: Current volume: 0~100 #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d" msgstr "%d. bolumena" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album ezezaguna" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista ezezaguna" ================================================ FILE: po/fa.po ================================================ # Persian translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # sohrabbehdani , 2024. # # SPDX-FileCopyrightText: 2024 Sohrab Behdani msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-24 11:17+0000\n" "PO-Revision-Date: 2025-01-13 16:21+0330\n" "Last-Translator: Danial Behzadi \n" "Language-Team: Persian \n" "Language: fa\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.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "بی‌وقفه" #: data/app.desktop.in:4 msgid "Music Player" msgstr "پخش‌کنندهٔ آهنگ" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "پخش شیک آهنگ‌هایتان" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;آهنگ;صدا;پخش‌کننده;رسانه;صوت;سیاهه‌پخش;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, focuses on " "large music collection." msgstr "" "بی‌وقفه (G4Music) پخش‌کنندهٔ آهنگی سبک است که با GTK4 نوشته شده و بر مجموعه‌های آهنگ " "بزرگ تمرکز دارد." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "ویژگی‌ها" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols (depends on " "GIO and GStreamer)." msgstr "" "پشتیبانی از بیش‌تر گونه‌های پروندهٔ آهنگ، سامبا و دیگر شیوه‌نامه‌های دوردست (بسته به " "GIO و GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, monitor " "local changes." msgstr "بار کردن سریع و تجزیهٔ هزاران پروندهٔ آهنگ در چند ثانیه. پایش تغییرات محلی." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "استفادهٔ کم حافظه برای مجموعهٔ آهنگ بزرگ با طرح جلد (تعبیه شده و خارجی). بدون " "نگه‌داری انبارهٔ بندانگشی." #: data/app.metainfo.xml.in:30 msgid "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "گروه‌بندی و چینش بر اساس آلبوم، هنرمند و عنوان. بر زدن سیاهه. جست‌وجوی متن کامل." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, Mobile)." msgstr "میانای کاربری سازوا برای صفحه‌های مختلف (میزکار، رایانک، تلفن همراه)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "طرح جلد مات گوسی به عنوان پس‌زمینه. پیروی از حالت تیره و روشن گنوم." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add to " "another playlist." msgstr "" "پشیبانی از ایجاد و ویرایشسیاهه‌های پخش، کشیدن طرح جلد برای تغییر ترتیب یا افزودن " "به سیاههٔ پخشی دیگر." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "پشتیانی از کشیدن و رها کردن با دیگر کاره‌ها." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "پشتیبانی از نمایش سطح اوج صدا." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "پشتیبانی از پخش بی‌وقفه." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "پشتیبانی از عادی سازی حجم صدا با ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "پشتیانی از سامانهٔ صوتی مشخّص." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "پشتیبانی از واپایش MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "پنجرهٔ اصلی" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "نمای آلبوم‌ها" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "نمای پخش کردن" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "نمایهٔ سیاههٔ پخش" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "عمومی" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "نمایش میان‌برها" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "ترجیحات" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "خروج" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "نمایش برچسب‌ها" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "پخش" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "پخش یا مکث" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "پخش بعدی" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "پخش پیشین" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "سیاههٔ پخش" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "جست‌وجو" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "تغییر ‌حالت چینش" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "بازخوانی کتابخانه" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "ذخیرهٔ سیاهه" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:632 src/ui/store-panel.vala:421 msgid "Back" msgstr "بازگشت" #: src/gtk/preferences.ui:10 msgid "General" msgstr "عمومی" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "حالت محوی پس‌زمینه" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "نمای‌ سیاههٔ پخش فشرده" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "نمای شبکه‌ای برای هنرمند‌ها و آلبوم‌ها" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "یک بار زدن برای فعّال کردن مورد" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "ادامهٔ پخش پس از بستن پنجره" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "کتابخانه" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "بار کردن آهنگ‌ها از شاخه" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "پایش تغییرات پرونده‌های محلی" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "بار کردن بندانگشتی‌ها برای پرونده‌های غیرمحلی" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "ممکن است موجب کندی و افزایش استفاده از شبکه شود" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "پخش" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "نمایش سطح اوج صدا" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "نمایش نویسه‌ها" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "چرخاندن طرح جلد آلبوم" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "به کار انداختن پخش بی‌وقفه" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "عادی سازی حجم صدا با ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "ترجیح سینک صدای GStreamer" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "فهرست اصلی" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "جست‌وجو" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "چینش بر پایه" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_طرحوارهٔ رنگی" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_سامانه" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_روشن" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_تیره" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_بازخوانی کتابخانه" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "گزینش _چندگانه" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_ذخیرهٔ سیاهه" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_ترجیحات" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "میان‌برهای _صفحه‌کلید" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_درباره‌" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "آلبوم" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "هنرمند" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "هنرمند یا آلبوم" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "عنوان" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "اخیر" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "بُرزنی" #: src/action-handles.vala:114 msgid "Image Files" msgstr "پرونده‌های تصویر" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "برون‌ریزی موفّق طرح جلد" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "برون‌ریزی طرح جلد شکست خورد" #: src/application.vala:346 msgid "Save playlist failed" msgstr "ذخیرهٔ سیاههٔ پخش شکست خورد" #: src/application.vala:470 msgid "Playlist Files" msgstr "پرونده‌های سیاههٔ پخش" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "ذخیرهٔ موفّق سیاههٔ پخش" #: src/application.vala:620 msgid "Keep playing" msgstr "ادامهٔ پخش" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "پخش‌کنندهٔ آهنگ سریع، زیبا و سبک نوشته شده با GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "سهراب بهدانی \n" "دانیال بهزادی " #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "نه" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "بله" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "سیاههٔ پخش دستگاری شده. ذخیره شود؟" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:405 src/ui/music-list.vala:421 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "پخش در _بعدی" #: src/ui/music-list.vala:407 src/ui/music-list.vala:423 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "افزودن به _صف" #: src/ui/music-list.vala:408 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "افزودن به _سیاههٔ پخش…" #: src/ui/music-list.vala:409 src/ui/music-list.vala:427 msgid "_Remove" msgstr "_برداشتن" #: src/ui/music-list.vala:428 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "انداختن به _زباله‌دان" #: src/ui/music-list.vala:636 msgid "Select All" msgstr "گزینش همه" #: src/ui/music-list.vala:653 msgid "Play at Next" msgstr "پخش در بعدی" #: src/ui/music-list.vala:660 msgid "Add to Queue" msgstr "افزودن به صف" #: src/ui/music-list.vala:667 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "افزودن به سیاههٔ پخش" #: src/ui/music-list.vala:674 msgid "Remove" msgstr "برداشتن" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "پخش" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "پخش _کاتوره‌ای" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "نمایش _پروندهٔ سیاهه" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "جست‌وجوی عنوان" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "جست‌وجوی آلبوم" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "جست‌وجوی هنرمند" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "نمایش _پروندهٔ طرح جلد" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_برون‌ریزی طرح جلد" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "نمایش _برچسب‌ها…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "نمایش _پروندهٔ آهنگ" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "تکرار تکی" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "پخش پیشین" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "پخش یا مکث" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "پخش بعدی" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "کشیدن و رها سازی پرونده‌های آهنگ‌ها در این‌جا،\n" "یا تغییر مکان آهنگ‌ها: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "سیاههٔ پخش جدید" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:703 #, c-format msgid "No playlist found in %s" msgstr "هیچ سیاههٔ پخشی در %s پیدا نشد." #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "هرگز" #: src/ui/preferences.vala:44 msgid "Always" msgstr "همیشه" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "فقط کار هنری" #: src/ui/preferences.vala:66 msgid "Track" msgstr "قطعه" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "پخش کردن" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "هنرمندان" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "آلبوم‌ها" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "سیاهه‌‌های پخش" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "رونوشت" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "حجم صدا %d٪" #: src/ui/window.vala:119 msgid "Show" msgstr "نمایش" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "آلبوم ناشناخته" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "هنرمند ناشناس" #~ msgid "Nanling" #~ msgstr "نانلینگ" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "کشیدن-رها کردن از پرونده‌های کنوم، نمایش آهنگ در پرونده‌ها." #~ msgid "Play music" #~ msgstr "پخش آهنگ‌ها" #~ msgid "Prefer dark theme" #~ msgstr "ترجیح پوستهٔ تیره" #~ msgid "G4Music" #~ msgstr "آهنگ‌های جی‌فور" ================================================ FILE: po/fi.po ================================================ # Finnish translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Jiri Grönroos , 2023. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-11-11 20:34+0200\n" "Last-Translator: Jiri Grönroos \n" "Language-Team: Finnish \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: Poedit 3.4.4\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Musiikkisoitin" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Toista musiikkia hienostuneesti" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;musiikki;ääni;soittolista;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (tunnettu myös nimellä G4Music) on kevyt musiikintoistosovellus, " "joka on kirjoitettu GTK4:llä, ja sen huomion kohteena on suuret " "musiikkikokoelmat." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Ominaisuudet" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Tukee useimpia musiikkitiedostoja sekä Samba- ja muita etäyhteyskäytäntöjä " "(riippuvuus GIO:on ja Gstreameriin)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Tuhansien musiikkitiedostojen lataaminen ja jäsentäminen erittäin nopeasti, " "tarkkailee paikallisia muutoksia." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Matala muistinkäyttö suurilla musiikkikokoelmilla ja albumien kansikuvilla " "(upotettuna tai ulkoisesti), ei pikkukuvien välimuistia talletettavaksi." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Ryhmitä ja järjestä albumin, esittäjän tai kappaleen mukaan, sekoita " "listoja, koko tekstin haku." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Mukautuva käyttöliittymä eri näyttökooille (työpöytä, tabletti, mobiili)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "Sumennettu kansikuva taustaksi, seuraa Gnomen vaaleaa ja tummaa tilaa." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Tukee soittolistojen luomista ja muokkausta, vedä kansikuva vaihtaaksesi " "järjestystä tai lisätäksesi toiseen soittolistaan." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Tukee vedä ja pudota -toimintoa muiden sovellusten kanssa." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Tukee äänihuippujen visualisointia." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Tukee tautonta toistoa." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Tukee äänenvoimakkuuden normalisointia ReplayGainilla." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Tukee määritettyä ääniallasta." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Tukee MPRIS-ohjaimia." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Pääikkuna" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albuminäkymä" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Toistonäkymä" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Soittolistanäkymä" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Yleiset" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Näytä pikanäppäimet" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Asetukset" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Lopeta" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Näytä tunnisteet" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Toisto" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Toista/keskeytä" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Toista seuraava" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Toista edellinen" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Soittolista" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Etsi" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Järjestys päälle/pois" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Lataa kirjasto uudelleen" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Tallenna lista" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Takaisin" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Yleiset" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Taustan sumennustila" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompakti soittolistanäkymä" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Ruudukkonäkymä esittäjille/albumeille" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Kertanapsautus aktivoi kohteen" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Jatka toistamista ikkunan sulkemisen jälkeen" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Kirjasto" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Lataa musiikki kansiosta" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Tarkkaile paikallisia tiedostomuutoksia" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Lataa pienoiskuvat myös muille kuin paikallisille tiedostoille" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Saattaa aiheuttaa hidastumista ja paljon verkkoliikennettä" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Toisto" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Näytä äänen" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Näytä merkit" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Kierrä albumin kansikuvaa" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Käytä tauotonta toistoa" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalisoi äänenvoimakkuus ReplayGainilla" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Suosi GStreamerin ääniallasta" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Päävalikko" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Etsi" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Järjestä" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Väriteema" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_Järjestelmä" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Vaalea" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Tumma" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "_Lataa kirjasto uudelleen" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Monivalinta" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Tallenna lista" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Asetukset" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Pikanäppäimet" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Tietoja" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Albumi" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Esittäjä" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Esittäjä/albumi" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Nimi" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Viimeisimmät" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Sekoita" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Kuvatiedostot" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Kansi viety onnistuneesti" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Kannen vienti epäonnistui" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Soittolistan tallentaminen epäonnistui" #: src/application.vala:447 msgid "Playlist Files" msgstr "Soittolistatiedostot" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Soittolista tallennettu" #: src/application.vala:597 msgid "Keep playing" msgstr "Jatka toistamista" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Nopea ja kevyt musiikkisoitin GTK4:llä toteutettuna." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Jiri Grönroos" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Ei" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Kyllä" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Soittolistaa on muokattu, tallennetaanko se?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Toista _seuraavaksi" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Lisää _jonoon" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Lisää _soittolistalle…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Poista" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Siirrä roskakoriin" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Valitse kaikki" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Toista seuraavaksi" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Lisää jonoon" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Lisää soittolistalle" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Poista" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Toista" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Satunnaistoisto" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Näytä _listatiedosto" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Etsi nimeä" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Etsi albumia" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Etsi esittäjää" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Näytä _kansitiedosto" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Vie kansi" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Näytä _tunnisteet…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Näytä _musiikkitiedosto" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Kertakierto" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Toista edellinen" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Toista/keskeytä" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Toista seuraava" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Vedä ja pudota musiikkitiedostot tähän\n" "tai vaihda musiikin sijaintia: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Uusi soittolista" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "Soittolistaa %s ei löytynyt" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Ei koskaan" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Aina" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Vain kuvitus" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Kappale" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Toistetaan" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Esittäjät" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albumit" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Soittolistat" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopioi" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Äänenvoimakkuus %d% %" #: src/ui/window.vala:112 msgid "Show" msgstr "Näytä" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Tuntematon albumi" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Tuntematon esittäjä" #~ msgid "G4Music" #~ msgstr "G4Music" #~ msgid "Play music" #~ msgstr "Toista musiikkia" #~ msgid "Prefer dark theme" #~ msgstr "Suosi tummaa teemaa" ================================================ FILE: po/fr.po ================================================ # French translation for G4Music. # Copyright (C) 2022 # This file is distributed under the same license as the G4Music package. # Aurélien Hamy , 2022. # Naqua Darazaki , 2022. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/-/issues\n" "POT-Creation-Date: 2023-08-16 08:46+0000\n" "PO-Revision-Date: 2023-08-16 08:46+0000\n" "Last-Translator: Alexandre Franke \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/app.desktop.in:3 src/application.vala:232 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Lecteur de musique" #: data/app.desktop.in:5 msgid "Play music" msgstr "Jouer de la musique" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musique;son;lecteur;média;audio;liste de lecture;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Général" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Afficher les raccourcis" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Préférences" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Quitter" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Lecture" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Lire/Suspendre" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Lire la prochaine piste" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Lire la piste précédente" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Playlist" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Recherche" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Basculer le tri" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Recharger la collection" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:340 msgid "Back" msgstr "Précédent" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Général" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Préférer le thème sombre" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Mode de flou du fond" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Vue compacte de la liste de lecture" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "Vue en grille pour les artistes/albums" #: src/gtk/preferences.ui:56 src/application.vala:432 msgid "Keep playing after window closed" msgstr "Continuer la lecture après la fermeture de la fenêtre" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Collection" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Charger la musique depuis le dossier" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Surveiller les changements sur les fichiers locaux" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Charger les miniatures des fichier externes" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Peut causer des ralentissements et une utilisation excessive du réseau" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Lecture" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Afficher les pics sonores" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Afficher les caractères" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Tourner la pochette d’album" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Activer la lecture sans interruption" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Utiliser ReplayGain pour normaliser le volume" #: src/gtk/preferences.ui:164 #, fuzzy msgid "Prefer audio sink" msgstr "Utiliser la sortie audio pipewire" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu principal" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Critère de tri" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Recherche" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "_Recharger la collection" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Préférences" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "_Raccourcis clavier" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "À _propos" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artiste" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artiste/album" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Titre" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Récent" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Mélanger" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Un lecteur de musique GTK4 rapide, joli et léger." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "Aurélien Hamy\n" "Naqua Darazaki\n" "Alexandre Franke" #: src/application.vala:613 msgid "Keep playing" msgstr "Continuer à jouer" #: src/ui/music-widgets.vala:251 src/ui/music-widgets.vala:292 #: src/ui/music-widgets.vala:302 #, fuzzy msgid "Play at Next" msgstr "Lire la piste suivante" #: src/ui/music-widgets.vala:252 src/ui/music-widgets.vala:291 #: src/ui/music-widgets.vala:301 src/ui/store-panel.vala:345 msgid "Play" msgstr "Lire" #: src/ui/music-widgets.vala:255 src/ui/play-panel.vala:232 msgid "Show _Cover File" msgstr "Afficher le fichier couverture" #: src/ui/music-widgets.vala:257 src/ui/play-panel.vala:234 msgid "_Export Cover" msgstr "_Exporter la couverture" #: src/ui/music-widgets.vala:294 msgid "Show List File" msgstr "Afficher le fichier liste" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:57 msgid "Search Title" msgstr "Recherche le titre" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:55 msgid "Search Album" msgstr "Recherche l’album" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:56 msgid "Search Artist" msgstr "Rechercher l'artiste" #: src/ui/music-widgets.vala:311 msgid "_Show Music File" msgstr "_Afficher le fichier musique" #: src/ui/play-bar.vala:75 #, fuzzy msgid "Repeat Music" msgstr "Répéter la piste" #: src/ui/play-bar.vala:85 msgid "Play Previous" msgstr "Lire la piste précédente" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:91 msgid "Play/Pause" msgstr "Lire/Suspendre" #: src/ui/play-bar.vala:98 msgid "Play Next" msgstr "Lire la piste suivante" #: src/ui/play-panel.vala:275 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Glissez et déposez vos fichiers de musique ici,\n" "ou changez l’emplacement de la musique : " #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "Jamais" #: src/ui/preferences.vala:46 msgid "Always" msgstr "Toujours" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "Art uniquement" #: src/ui/preferences.vala:67 msgid "Track" msgstr "Piste" #: src/ui/store-panel.vala:74 msgid "Playing" msgstr "Lecture" #: src/ui/store-panel.vala:79 msgid "Artists" msgstr "Artiste" #: src/ui/store-panel.vala:84 msgid "Albums" msgstr "Album" #: src/ui/store-panel.vala:374 msgid "Playlists" msgstr "Liste de lecture" #: src/ui/volume-button.vala:32 msgid "Volume: " msgstr "Volume : " #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album inconnu" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artiste inconnu" ================================================ FILE: po/g4music.pot ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-11 09:34+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "" #: data/app.desktop.in:4 msgid "Music Player" msgstr "" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "" #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "" #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "" #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "" #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "" #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "" #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "" #: src/gtk/preferences.ui:10 msgid "General" msgstr "" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "" #: src/action-handles.vala:114 msgid "Image Files" msgstr "" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "" #: src/application.vala:350 msgid "Save playlist failed" msgstr "" #: src/application.vala:447 msgid "Playlist Files" msgstr "" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "" #: src/application.vala:597 msgid "Keep playing" msgstr "" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "" #: src/ui/preferences.vala:44 msgid "Always" msgstr "" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "" #: src/ui/preferences.vala:66 msgid "Track" msgstr "" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "" #: src/ui/window.vala:112 msgid "Show" msgstr "" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "" ================================================ FILE: po/he.po ================================================ # Hebrew translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Yosef Or Boczko , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-24 11:17+0000\n" "PO-Revision-Date: 2025-01-09 09:25+0200\n" "Last-Translator: Yosef Or Boczko \n" "Language-Team: Hebrew \n" "Language: he\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 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? " "2 : 3);\n" "X-Generator: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "נגן מוזיקה" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "לנגן את המוזיקה שלך באלגנטיות" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "מוסיקה;מוזיקה;שמע;צליל;נגן;מדיה;רשימת נגינה;רשימות נגינה;רשימת השמעה;" "פלייליסט;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "‏Gapless (הידוע גם בשם: G4Music) הוא נגן מוזיקה קל משקל שנכתב ב־GTK4, המתמקד " "באוספי מוזיקה גדולים." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "תכונות" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "תמיכה ברוב סוגי קבצי המוזיקה, Samba ופרוטוקולים מרוחקים אחרים (תלוי ב־GIO " "ו־GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "טעינה ופענוח מהירים של אלפי קובצי מוזיקה תוך שניות ספורות, מעקב אחר שינויים " "מקומיים." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "שימוש מועט בזיכרון עבור אוסף מוזיקה גדול עם עטיפות אלבומים (מוטבעות " "וחיצוניות), ללא צורך באחסון מטמון של תמונות ממוזערות." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "קיבוץ ומיון לפי אלבום/אומן/כותרת, ערבוב רשימה, חיפוש טקסט מלא." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "ממשק משתמש מסתגל ומותאם למסכים שונים (מחשב שולחני, מחשב לוח, נייד)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "עטיפת אלבום מטושטשת כרקע, מעקב אחר סגנון מערכת בהיר/כהה של GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "תמיכה ביצירה ועריכה של רשימות נגינה, גרירת העטיפה תשנה את הסדר או תוסיף " "לרשימת נגינה אחרת." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "תמיכה בגרירה מיישומים אחרים ואליהם." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "תמיכה בהדמיית שיאי אודיו." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "תמיכה בנגינה ללא הפסקות." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "תמיכה באיזון עצמת קול עם ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "תמיכה בשקע השמע שצוין." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "תמיכה בשליטת MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "חלון ראשי" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "תצוגת אלבומים" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "תצוגת נגינה" # msgctxt "shortcut window" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "תצוגת רשימת נגינה" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "כללי" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "הצגת צירופי מקשים" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "העדפות" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "יציאה" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "הצגת תגיות" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "בִצוע מוקלט" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "נגינה/השהיה" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "ניגון הרצועה הבאה" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "ניגון הרצועה הקודמת" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "רשימת נגינה" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "חיפוש" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "שינוי סדר נגינה" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "טעינה הספרייה מחדש" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "שמירת הרשימה" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:632 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "אחורה" #: src/gtk/preferences.ui:10 msgid "General" msgstr "כללי" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "מצב רקע מטושטש" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "תצוגת רשימת נגינה קומפקטית" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "תצוגת רשת לאלבומים/אומנים" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "לחיצה אחת מפעילה פעיל" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "להמשיך להשמיע לאחר סגירת החלון" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "ספרייה" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "טעינת מוזיקה מתיקייה" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "מעקב אחר שינוי בקבצים" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "טעינת תמונות ממוזערות לקבצים לא מקומיים" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "עשוי לגרום להאטה ולשימוש יתר ברשת" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "בִצוע מוקלט" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "הצגת עצמת שיאי שמע" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "תווים להצגה" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "סיבוב כיסוי אלבום" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "לאפשר נגינה ללא הפסקה בין רצועות" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "איזון עצמת שמע עם ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "העדפת יציאת שמע של GStreamer" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "תפריט ראשי" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "חיפוש" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "מיון לפי" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "ע_רכת צבעים" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "מ_ערכת" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_בהירה" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_כהה" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_טעינה מחדש של הספרייה" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "רב _בחירה" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_שמירת הרשימה" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_העדפות" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_צירופי מקשים" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "על _אודות" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "אלבום" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "אומן" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "אומן האלבום" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "כותרת" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "הושמעו לאחרונה" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "ערבוב" #: src/action-handles.vala:114 msgid "Image Files" msgstr "קובצי תמונה" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "העטיפה יוצאה בהצלחה" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "ייצוא עטיפה נכשל" #: src/application.vala:346 msgid "Save playlist failed" msgstr "שמירת רשימת הנגינה נכשלה" # msgctxt "shortcut window" #: src/application.vala:470 msgid "Playlist Files" msgstr "קובצי רשימת נגינה" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "רשימת הנגינה נשמרה בהצלחה" #: src/application.vala:620 msgid "Keep playing" msgstr "להמשיך לנגן" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "נגן מוזיקה קל, מהיר וגמיש שנכתב ב־GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "יוסף אור בוצ׳קו \n" "מיזם תרגום GNOME לעברית https://l10n.gnome.org/teams/he/" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "לא" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "כן" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "רשימה הנגינה שונתה, לשמור אותה?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:405 src/ui/music-list.vala:421 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "הבא שית_נגן" #: src/ui/music-list.vala:407 src/ui/music-list.vala:423 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "הוספה ל_תור" # msgctxt "shortcut window" #: src/ui/music-list.vala:408 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "הוספה לרשימת _נגינה…" #: src/ui/music-list.vala:409 src/ui/music-list.vala:427 msgid "_Remove" msgstr "ה_סרה" #: src/ui/music-list.vala:428 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "העברה ל_אשפה" #: src/ui/music-list.vala:636 msgid "Select All" msgstr "בחירת הכול" #: src/ui/music-list.vala:653 msgid "Play at Next" msgstr "הבא בתור" #: src/ui/music-list.vala:660 msgid "Add to Queue" msgstr "הוספה לתור" # msgctxt "shortcut window" #: src/ui/music-list.vala:667 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "הוספה לרשימת נגינה" #: src/ui/music-list.vala:674 msgid "Remove" msgstr "הסרה" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "ניגון" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "נגינה א_קראית" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "הצגת _קובצי הרשימה" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "חיפוש כותרת" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "חיפוש אלבום" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "חיפוש אומן" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "הצגת קובץ _עטיפה" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_ייצוא כיסוי אלבום" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "הצגת _תגיות…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "ה_צגת קובץ מוזיקה" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "השמעה חוזרת של הרצועה" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "ניגון הרצועה הקודמת" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "נגינה/השהיה" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "ניגון הרצועה הבאה" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "יש לגרור ולשחרר לכאן קובצי מוזיקה,\n" "או לשנות את מיקום המוזיקה: " # msgctxt "shortcut window" #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "רשימת נגינה חדשה" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:703 #, c-format msgid "No playlist found in %s" msgstr "לא נמצאו רשימות נגינה ב־%s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "לעולם לא" #: src/ui/preferences.vala:44 msgid "Always" msgstr "תמיד" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "עם אומנות בלבד" #: src/ui/preferences.vala:66 msgid "Track" msgstr "רצועה" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "נגינה" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "אומנים" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "אלבומים" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "רשימות נגינה" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "העתקה" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "עצמת שמע ‎%d%%" #: src/ui/window.vala:119 msgid "Show" msgstr "הצגה" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "אלבום לא ידוע" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "אומן לא ידוע" #~ msgid "Play music" #~ msgstr "השמעת מוזיקה" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "גרירה ושחרור מקובצי GNOME, הצגת מוזיקה בקבצים." #~ msgid "Prefer dark theme" #~ msgstr "להשתמש בערכת עיצוב כהה" #~ msgid "Close" #~ msgstr "סגירה" #~ msgid "G4Music" #~ msgstr "G4Music" ================================================ FILE: po/hi.po ================================================ # Hindi translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Scrambled777 , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-07-20 09:24+0000\n" "PO-Revision-Date: 2024-07-21 09:56+0530\n" "Last-Translator: Scrambled777 \n" "Language-Team: Hindi \n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-DL-Lang: hi\n" "X-DL-Module: g4music\n" "X-DL-Branch: master\n" "X-DL-Domain: po\n" "X-DL-State: None\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Gtranslator 46.1\n" #: data/app.desktop.in:3 src/application.vala:237 src/main.vala:24 msgid "Gapless" msgstr "गैपलेस" #: data/app.desktop.in:4 msgid "Music Player" msgstr "म्यूजिक प्लेयर" #: data/app.desktop.in:5 msgid "Play music" msgstr "संगीत बजाएं" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "संगीत;ध्वनि;प्लेयर;मीडिया;ऑडियो;प्लेलिस्ट;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "सामान्य" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "शॉर्टकट दिखाएं" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "प्राथमिकताएं" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "बंद करें" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "प्लेबैक" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "बजाएं/रोकें" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "अगला बजाएं" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "पिछला बजाएं" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "प्लेलिस्ट" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "खोजें" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "छंटाई टॉगल करें" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "लाइब्रेरी पुनः लोड करें" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:350 msgid "Back" msgstr "पीछे" #: src/gtk/preferences.ui:10 msgid "General" msgstr "सामान्य" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "गहरी थीम को प्राथमिकता" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "पृष्ठभूमि धुंधला मोड" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "सघन प्लेलिस्ट दृश्य" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "कलाकारों/एल्बमों के लिए ग्रिड दृश्य" #: src/gtk/preferences.ui:56 src/application.vala:428 msgid "Keep playing after window closed" msgstr "विंडो बंद होने के बाद भी बजाते रहें" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "लाइब्रेरी" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "फोल्डर से संगीत लोड करें" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "स्थानीय फाइल परिवर्तनों पर नज़र रखें" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "गैर-स्थानीय फाइलों के लिए थंबनेल लोड करें" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "धीमेपन और अत्यधिक नेटवर्क उपयोग का कारण बन सकता है" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "प्लेबैक" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "ऑडियो चरम स्तर दिखाएं" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "वर्ण प्रदर्शित करें" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "एल्बम कवर घुमाएं" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "अंतराल रहित प्लेबैक सक्षम करें" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "रीप्लेगैन के साथ वॉल्यूम सामान्य करें" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "GStreamer ऑडियो सिंक को प्राथमिकता दें" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "मुख्य मेनू" #: src/gtk/store-panel.ui:29 msgid "Search" msgstr "खोजें" #: src/gtk/store-panel.ui:35 msgid "Sort By" msgstr "ऐसे छांटें" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "लाइब्रेरी पुनः लोड करें (_R)" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "प्राथमिकताएं (_P)" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "कीबोर्ड शॉर्टकट (_K)" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "परिचय (_A)" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "एल्बम" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "कलाकार" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "कलाकार/एल्बम" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "शीर्षक" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "हालिया" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "शफ़ल" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "GTK4 में लिखा गया एक तेज़, धाराप्रवाह, हल्के वजन वाला म्यूजिक प्लेयर।" #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "Scrambled777 " #: src/application.vala:625 msgid "Keep playing" msgstr "बजते रहें" #. Translators: Play this music at next position of current playing music #: src/ui/music-widgets.vala:211 src/ui/music-widgets.vala:252 #: src/ui/music-widgets.vala:262 msgid "Play at Next" msgstr "आगे यह बजाएं" #: src/ui/music-widgets.vala:212 src/ui/music-widgets.vala:251 #: src/ui/music-widgets.vala:261 src/ui/store-panel.vala:355 msgid "Play" msgstr "बजाएं" #: src/ui/music-widgets.vala:215 src/ui/play-panel.vala:130 msgid "Show _Cover File" msgstr "कवर फाइल दिखाएं (_C)" #: src/ui/music-widgets.vala:217 src/ui/play-panel.vala:132 msgid "_Export Cover" msgstr "कवर निर्यात (_E)" #: src/ui/music-widgets.vala:254 msgid "Show List File" msgstr "सूची फाइल दिखाएं" #: src/ui/music-widgets.vala:268 src/ui/play-panel.vala:61 msgid "Search Title" msgstr "शीर्षक खोजें" #: src/ui/music-widgets.vala:269 src/ui/play-panel.vala:59 msgid "Search Album" msgstr "एल्बम खोजें" #: src/ui/music-widgets.vala:270 src/ui/play-panel.vala:60 msgid "Search Artist" msgstr "कलाकार खोजें" #: src/ui/music-widgets.vala:271 msgid "_Show Music File" msgstr "संगीत फाइल दिखाएं (_S)" #. Translators: single loop the current music #: src/ui/play-bar.vala:74 msgid "Single Loop" msgstr "एकल लूप" #: src/ui/play-bar.vala:84 msgid "Play Previous" msgstr "पिछला बजाएं" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:90 msgid "Play/Pause" msgstr "बजाएं/रोकें" #: src/ui/play-bar.vala:97 msgid "Play Next" msgstr "अगला बजाएं" #: src/ui/play-panel.vala:285 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "संगीत फाइलों को यहां खींचें और छोड़ें,\n" "या संगीत स्थान बदलें: " #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "कभी नहीं" #: src/ui/preferences.vala:46 msgid "Always" msgstr "हमेशा" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "केवल कला" #: src/ui/preferences.vala:67 msgid "Track" msgstr "ट्रैक" #: src/ui/store-panel.vala:75 msgid "Playing" msgstr "बज रहा है" #: src/ui/store-panel.vala:80 msgid "Artists" msgstr "कलाकार" #: src/ui/store-panel.vala:85 msgid "Albums" msgstr "एलबम" #: src/ui/store-panel.vala:384 msgid "Playlists" msgstr "प्लेलिस्ट" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "आवाज़ %d%% " #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "अज्ञात एल्बम" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "अज्ञात कलाकार" ================================================ FILE: po/hu.po ================================================ # Hungarian translation for g4music. # Copyright (C) 2023, 2024, 2025 Free Software Foundation, Inc. # This file is distributed under the same license as the g4music package. # # Balázs Úr , 2023, 2024, 2025. msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2025-09-10 13:36+0000\n" "PO-Revision-Date: 2025-09-11 09:31+0200\n" "Last-Translator: Balázs Úr \n" "Language-Team: Hungarian \n" "Language: hu\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 24.12.3\n" #: data/app.desktop.in:2 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "Szünetmentes" #: data/app.desktop.in:3 msgid "Music Player" msgstr "Zenelejátszó" #: data/app.desktop.in:4 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Zenék elegáns lejátszása" #: data/app.desktop.in:11 msgid "music;sound;player;media;audio;playlist;" msgstr "zene;zaj;lejátszó;média;hang;lejátszási lista;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "A Szünetmentes (más néven: G4Music) egy GTK4-ben írt, könnyűsúlyú zenelejátszó" ", amely nagy zenei gyűjteményekre összpontosít." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Funkciók" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Támogatja a legtöbb zenefájltípust, a Sambát és egyéb távoli protokollokat (a " "GIO-tól és a GStreamertől függően)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Több ezer zenefájl gyors betöltése és feldolgozása néhány másodperc alatt, a h" "elyi változtatások figyelése." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Alacsony memóriahasználat albumborítókkal (beágyazott és külső) rendelkező nag" "y zenei gyűjteményeknél, tárolandó bélyegkép-gyorsítótárak nélkül." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Csoportosítás és rendezés album, előadó vagy cím szerint, véletlenszerű lejáts" "zási lista, teljes szöveges keresés." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Folyékony, adaptív felhasználói felület különböző képernyőkhöz (asztali számít" "ógép, táblagép, mobiltelefon)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Gauss-elmosást alkalmazott borító háttérként, ami követi a GNOME világos vagy " "sötét módját." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Támogatja a lejátszási listák létrehozását és szerkesztését, a borító húzásáva" "l megváltoztathatja a sorrendet vagy hozzáadhatja egy másik lejátszási listáho" "z." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Támogatja a fogd és vidd funkciót más alkalmazásokkal." #: data/app.metainfo.xml.in:35 #| msgid "Show audio peak level" msgid "Supports audio peaks visualizer." msgstr "Támogatja a hangcsúcsok grafikus megjelenítését." #: data/app.metainfo.xml.in:36 #| msgid "Enable gapless playback" msgid "Supports gapless playback." msgstr "Támogatja a szünetmentes lejátszást." #: data/app.metainfo.xml.in:37 #| msgid "Normalize volume with ReplayGain" msgid "Supports normalizing volume with ReplayGain." msgstr "Támogatja a hangerő normalizálását hangerő-korrekcióval." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Támogatja a megadott hangelnyelőt." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Támogatja az MPRIS-vezérlést." #: data/app.metainfo.xml.in:50 #| msgid "Main Menu" msgid "Main window" msgstr "Főablak" #: data/app.metainfo.xml.in:54 #| msgid "Albums" msgid "Albums view" msgstr "Albumok nézet" #: data/app.metainfo.xml.in:58 #| msgid "Playing" msgid "Playing view" msgstr "Lejátszás nézet" #: data/app.metainfo.xml.in:62 #| msgctxt "shortcut window" #| msgid "Playlist" msgid "Playlist view" msgstr "Lejátszási lista nézet" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Általános" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Gyorsbillentyűk megjelenítése" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Beállítások" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Kilépés" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Címkék megjelenítése" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Lejátszás" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Lejátszás vagy szüneteltetés" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Következő lejátszása" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Előző lejátszása" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Lejátszási lista" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Keresés" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Rendezés be- és kikapcsolása" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Gyűjtemény újratöltése" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Lista mentése" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:626 #: src/ui/store-panel.vala:420 msgid "Back" msgstr "Vissza" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Általános" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Háttér elmosásának módja" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Tömör lejátszási lista nézet" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Előadók vagy albumok rácsnézete" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Egy kattintás az elem aktiválásához" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "Lejátszás folytatása az ablak bezárása után" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Gyűjtemény" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Zenék betöltése mappából" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Helyi fájlváltozások figyelése" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Bélyegképek betöltése a nem helyi fájlokhoz" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Lassulást és túlzott hálózathasználatot okozhat" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Lejátszás" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Hang csúcsszintjének megjelenítése" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Karakterek megjelenítése" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Albumborító forgatása" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Szünetmentes lejátszás engedélyezése" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Hangerő normalizálása hangerő-korrekcióval" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "A GStreamer hangnyelőjének előnyben részesítése" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Főmenü" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Keresés" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Rendezési sorrend" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "S_zínséma" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_Rendszer" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Világos" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Sötét" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "Gyűjtemény újra_töltése" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "_Több kiválasztása" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "Lista _mentése" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Beállítások" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_Gyorsbillentyűk" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_Névjegy" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Előadó" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Előadó vagy album" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Cím" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Legutóbbi" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Keverés" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Képfájlok" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "A borító sikeresen exportálva" #: src/action-handles.vala:121 #| msgid "_Export Cover" msgid "Export cover failed" msgstr "Nem sikerült a borító exportálása" #: src/application.vala:346 #| msgid "Compact playlist view" msgid "Save playlist failed" msgstr "Nem sikerült a lejátszási lista mentése" #: src/application.vala:470 #| msgid "Playlists" msgid "Playlist Files" msgstr "Lejátszási lista fájlok" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "A lejátszási lista sikeresen elmentve" #: src/application.vala:620 msgid "Keep playing" msgstr "Lejátszás folytatása" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:52 msgid "translator-credits" msgstr "Úr Balázs , 2023, 2024, 2025." #: src/ui/dialogs.vala:98 src/ui/dialogs.vala:110 msgid "No" msgstr "Nem" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:110 msgid "Yes" msgstr "Igen" #: src/ui/music-list.vala:270 msgid "Playlist is modified, save it?" msgstr "A lejátszási lista megváltozott, elmenti?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:399 src/ui/music-list.vala:415 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 #| msgid "Play at Next" msgid "Play at _Next" msgstr "Lejátszás a _következőnél" #: src/ui/music-list.vala:401 src/ui/music-list.vala:417 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Hozzáadás _sorhoz" #: src/ui/music-list.vala:402 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Hozzáadás _lejátszási listához…" #: src/ui/music-list.vala:403 src/ui/music-list.vala:421 msgid "_Remove" msgstr "_Eltávolítás" #: src/ui/music-list.vala:422 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Áthelyezés a _kukába" #: src/ui/music-list.vala:630 msgid "Select All" msgstr "Összes kijelölése" #: src/ui/music-list.vala:647 msgid "Play at Next" msgstr "Lejátszás a következőnél" #: src/ui/music-list.vala:654 msgid "Add to Queue" msgstr "Hozzáadás sorhoz" #: src/ui/music-list.vala:661 src/ui/playlist-dialog.vala:19 #| msgctxt "shortcut window" #| msgid "Playlist" msgid "Add to Playlist" msgstr "Hozzáadás lejátszási listához" #: src/ui/music-list.vala:668 msgid "Remove" msgstr "Eltávolítás" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:428 msgid "Play" msgstr "Lejátszás" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Véletlenszerű lejátszás" #: src/ui/music-widgets.vala:285 #| msgid "Show List File" msgid "Show List _File" msgstr "Lista_fájl megjelenítése" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:63 msgid "Search Title" msgstr "Cím keresése" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:61 msgid "Search Album" msgstr "Album keresése" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:62 msgid "Search Artist" msgstr "Előadó keresése" #: src/ui/music-widgets.vala:314 #| msgid "Show _Cover File" msgid "Show Cover _File" msgstr "Borító_fájl megjelenítése" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "Borító _exportálása" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "_Címkék megjelenítése…" #: src/ui/music-widgets.vala:318 #| msgid "_Show Music File" msgid "Show Music _File" msgstr "Zene_fájl megjelenítése" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Ismételt lejátszás" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Előző lejátszása" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Lejátszás vagy szüneteltetés" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Következő lejátszása" #: src/ui/play-panel.vala:56 src/ui/store-panel.vala:84 msgid "Playing" msgstr "Lejátszás" #: src/ui/play-panel.vala:266 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Fogjon meg és ejtsen ide zenefájlokat,\n" "vagy változtassa meg a zenék helyét: " #: src/ui/playlist-dialog.vala:24 #| msgctxt "shortcut window" #| msgid "Playlist" msgid "New Playlist" msgstr "Új lejátszási lista" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:712 #, c-format msgid "No playlist found in %s" msgstr "Nem található lejátszási lista ebben: %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Soha" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Mindig" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Csak borító" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Szám" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Előadók" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albumok" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Lejátszási listák" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Másolás" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Hangerő %d%%" #: src/ui/window.vala:126 msgid "Show" msgstr "Megjelenítés" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Ismeretlen album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Ismeretlen előadó" ================================================ FILE: po/ia.po ================================================ # Interlingua translation for g4music. # Copyright (C) 2025 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # FIRST AUTHOR , YEAR. # Emilio Sepúlveda , 2025. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2025-09-11 07:33+0000\n" "PO-Revision-Date: 2025-09-23 16:19-0300\n" "Last-Translator: Emilio Sepúlveda \n" "Language-Team: Interlingua \n" "Language: ia\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-DL-Lang: ia\n" "X-DL-Module: g4music\n" "X-DL-Branch: master\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Gtranslator 48.0\n" #: data/app.desktop.in:2 data/app.metainfo.xml.in:4 src/application.vala:273 #: src/main.vala:24 msgid "Gapless" msgstr "" #: data/app.desktop.in:3 msgid "Music Player" msgstr "Reproductor de musica" #: data/app.desktop.in:4 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Reproduce tu musica con elegantia" #: data/app.desktop.in:11 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Functionalitates" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "" #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "" #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "" #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "" #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "" #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "" #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Fenestra principal" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Vista de albumes" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Vista de reproduction" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Vista de lista de reproduction" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "General" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Monstrar vias breve" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferentias" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Quitar" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Monstrar etiquettas" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Reproduction" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Reproducer/Pausar" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Reproducer sequente" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Reproducer precedente" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Lista de reproduction" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Cercar" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Commutar ordinamento" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Recargar bibliotheca" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Salvar lista" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:626 #: src/ui/store-panel.vala:420 msgid "Back" msgstr "Retro" #: src/gtk/preferences.ui:10 msgid "General" msgstr "General" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Vista de lista de reproduction compacte" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Vista de grillia pro artistas/albumes" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Clicca un vice pro activar le elemento" #: src/gtk/preferences.ui:56 src/application.vala:434 msgid "Keep playing after window closed" msgstr "Mantener reproduction depost le claudimento del fenestra" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotheca" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Cargar musica ab dossier" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Reproduction" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Monstrar characteres" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalisar volumine con ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Menu principal" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Cercar" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Ordinar per" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "Schema de _color" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_Systema" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "C_lar" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Obscur" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_Recargar bibliotheca" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "Selection _multiple" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_Salvar lista" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Preferentias" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "Vias breve de _claviero" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_A proposito" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Artista/Album" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Titulo" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Recente" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Miscer" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Files de imagine" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "" #: src/application.vala:346 msgid "Save playlist failed" msgstr "" #: src/application.vala:470 msgid "Playlist Files" msgstr "Files de lista de reproduction" #: src/application.vala:485 msgid "Save playlist successfully" msgstr "" #: src/application.vala:620 msgid "Keep playing" msgstr "Mantener le reproduction" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:52 msgid "translator-credits" msgstr "Emilio Sepúlveda " #: src/ui/dialogs.vala:98 src/ui/dialogs.vala:110 msgid "No" msgstr "No" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:110 msgid "Yes" msgstr "Si" #: src/ui/music-list.vala:270 msgid "Playlist is modified, save it?" msgstr "Le lista de reproduction es modificate, salvar lo?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:399 src/ui/music-list.vala:415 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "" #: src/ui/music-list.vala:401 src/ui/music-list.vala:417 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Adder al _cauda" #: src/ui/music-list.vala:402 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Adder al lista de re_production" #: src/ui/music-list.vala:403 src/ui/music-list.vala:421 msgid "_Remove" msgstr "_Remover" #: src/ui/music-list.vala:422 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Displaciar al i_mmunditia" #: src/ui/music-list.vala:630 msgid "Select All" msgstr "Seliger toto" #: src/ui/music-list.vala:647 msgid "Play at Next" msgstr "" #: src/ui/music-list.vala:654 msgid "Add to Queue" msgstr "Adder al cauda" #: src/ui/music-list.vala:661 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Adder al lista de reproduction" #: src/ui/music-list.vala:668 msgid "Remove" msgstr "Remover" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:428 msgid "Play" msgstr "Reproducer" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Reproduction aleatori" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:63 msgid "Search Title" msgstr "" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:61 msgid "Search Album" msgstr "" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:62 msgid "Search Artist" msgstr "" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Monstrar _file de musica" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Reproducer precedente" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Reproducer/Pausar" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Reproducer sequente" #: src/ui/play-panel.vala:56 src/ui/store-panel.vala:84 msgid "Playing" msgstr "" #: src/ui/play-panel.vala:266 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Nove lista de reproduction" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:712 #, c-format msgid "No playlist found in %s" msgstr "" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nunquam" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Sempre" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Pista" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Artistas" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albumes" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Listas de reproduction" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Copiar" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volumine %d%%" #: src/ui/window.vala:126 msgid "Show" msgstr "Monstrar" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album incognite" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista incognite" ================================================ FILE: po/id.po ================================================ # Indonesian translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Andika Triwidada , 2023. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/-/issues\n" "POT-Creation-Date: 2023-08-17 01:27+0000\n" "PO-Revision-Date: 2023-08-17 17:18+0700\n" "Last-Translator: Andika Triwidada \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.3.2\n" #: data/app.desktop.in:3 src/application.vala:232 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Pemutar Musik" #: data/app.desktop.in:5 msgid "Play music" msgstr "Putar musik" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musik;suara;pemutar;media;audio;daftar putar;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Umum" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Tampilkan Pintasan" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferensi" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Keluar" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Putar" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Putar/Jeda" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Putar Berikutnya" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Putar Sebelumnya" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Daftar Putar" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Pencarian" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Jungkitkan Urutan" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Muat Ulang Pustaka" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:340 msgid "Back" msgstr "Kembali" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Umum" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Lebih suka tema gelap" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Mode pengaburan latar" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Tampilan daftar putar ringkas" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "Tampilan kisi bagi artis/album" #: src/gtk/preferences.ui:56 src/application.vala:432 msgid "Keep playing after window closed" msgstr "Tetap memutar setelah jendela ditutup" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Pustaka" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Muat musik dari folder" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Pantau perubahan berkas lokal" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Muat gambar mini bagi berkas bukan lokal" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Bisa menyebabkan melambat dan penggunaan jaringan berlebihan" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Putar" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Tampilkan tingkat puncak audio" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Tampilkan karakter" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Putar sampul album" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Aktifkan putar balik tanpa jeda" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalkan volume dengan ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Lebih suka muara audio dari GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu Utama" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Urut Berdasarkan" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Pencarian" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "Muat U_lang Pustaka" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Preferensi" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "_Tombol Pintas" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "Tent_ang" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artis" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artis/Album" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Judul" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Terkini" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Acak" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Pemutar musik yang cepat, fasih, dan ringan yang ditulis dalam GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "Andika Triwidada , 2023" #: src/application.vala:613 msgid "Keep playing" msgstr "Tetap memutar" #. Translators: Play this music at next position of current playing music #: src/ui/music-widgets.vala:252 src/ui/music-widgets.vala:293 #: src/ui/music-widgets.vala:303 msgid "Play at Next" msgstr "Putar pada Berikutnya" #: src/ui/music-widgets.vala:253 src/ui/music-widgets.vala:292 #: src/ui/music-widgets.vala:302 src/ui/store-panel.vala:345 msgid "Play" msgstr "Putar" #: src/ui/music-widgets.vala:256 src/ui/play-panel.vala:231 msgid "Show _Cover File" msgstr "Tampilkan Berkas _Sampul" #: src/ui/music-widgets.vala:258 src/ui/play-panel.vala:233 msgid "_Export Cover" msgstr "_Ekspor Sampul" #: src/ui/music-widgets.vala:295 msgid "Show List File" msgstr "Tampilkan Berkas Daftar" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:55 msgid "Search Title" msgstr "Cari Judul" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:53 msgid "Search Album" msgstr "Cari Album" #: src/ui/music-widgets.vala:311 src/ui/play-panel.vala:54 msgid "Search Artist" msgstr "Cari Artis" #: src/ui/music-widgets.vala:312 msgid "_Show Music File" msgstr "Tampilkan Berka_s Musik" #. Translators: single loop the current music #: src/ui/play-bar.vala:76 msgid "Single Loop" msgstr "Pengulangan Tunggal" #: src/ui/play-bar.vala:86 msgid "Play Previous" msgstr "Putar Sebelumnya" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:92 msgid "Play/Pause" msgstr "Putar/Jeda" #: src/ui/play-bar.vala:99 msgid "Play Next" msgstr "Putar Berikutnya" #: src/ui/play-panel.vala:274 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Seret dan jatuhkan berkas musik di sini,\n" "atau ubah lokasi musik: " #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "Tidak pernah" #: src/ui/preferences.vala:46 msgid "Always" msgstr "Selalu" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "Hanya Seni" #: src/ui/preferences.vala:67 msgid "Track" msgstr "Lacak" #: src/ui/store-panel.vala:74 msgid "Playing" msgstr "Memutar" #: src/ui/store-panel.vala:79 msgid "Artists" msgstr "Artis" #: src/ui/store-panel.vala:84 msgid "Albums" msgstr "Album" #: src/ui/store-panel.vala:374 msgid "Playlists" msgstr "Daftar Putar" #. Translators: Current volume: 0~100 #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d" msgstr "Volume %d" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album Tak Dikenal" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artis Tak Dikenal" ================================================ FILE: po/it.po ================================================ # ITALIAN TANSLATION FOR G4MUSIC. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Albano Battistella , 2022,2023. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-26 21:16+0800\n" "PO-Revision-Date: 2023-08-14 12:29+0200\n" "Last-Translator: Albano Battistella \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.1.1\n" #: data/app.desktop.in:3 src/application.vala:225 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Lettore musicale" #: data/app.desktop.in:5 msgid "Play music" msgstr "Riproduci musica" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musica;suono;lettore;media;audio;playlist;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Generale" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Mostra scorciatoie" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferenze" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Esci" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Riproduzione" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Riproduci/Pausa" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Riproduci prossimo" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Riproduci precedente" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Riproduci" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Cerca" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Cambia l'ordine" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Ricarica libreria" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:290 msgid "Back" msgstr "Indietro" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Generale" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Preferisci il tema scuro" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Applica blur allo sfondo" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Visualizza playlist compatta" #: src/gtk/preferences.ui:44 src/application.vala:424 msgid "Keep playing after window closed" msgstr "Continua a riprodurre dopo aver chiuso la finestra" #: src/gtk/preferences.ui:57 msgid "Library" msgstr "Libreria" #: src/gtk/preferences.ui:61 msgid "Load music from folder" msgstr "Carica musica dalla cartella" #: src/gtk/preferences.ui:73 msgid "Monitor local file changes" msgstr "Monitora cambiamenti dei file locali" #: src/gtk/preferences.ui:85 msgid "Load thumbnails for non-local files" msgstr "Carica miniature per file non locali" #: src/gtk/preferences.ui:86 msgid "May cause slowdowns and excess network usage" msgstr "Potrebbe causare rallentamenti e un uso della rete eccessivo" #: src/gtk/preferences.ui:99 msgid "Playback" msgstr "Riproduci" #: src/gtk/preferences.ui:103 msgid "Show audio peak level" msgstr "Mostra il livello di picco dell'audio" #: src/gtk/preferences.ui:107 msgid "Display characters" msgstr "Mostra caratteri" #: src/gtk/preferences.ui:122 msgid "Rotate album cover" msgstr "Ruota copertina album" #: src/gtk/preferences.ui:134 msgid "Enable gapless playback" msgstr "Abilita la riproduzione senza interruzioni" #: src/gtk/preferences.ui:146 msgid "Normalize volume with ReplayGain" msgstr "Usa ReplayGain per normalizzare il volume" #: src/gtk/preferences.ui:152 msgid "Prefer audio sink" msgstr "Audio sink preferito" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu principale" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Ordina per" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Cerca" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "_Ricarica libreria" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Preferenze" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "_Scorciatoie da tastiera" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "_Informazioni" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:64 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artista/Album" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Titolo" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Recente" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Mescola" #: src/action-handles.vala:188 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Un lettore musicale veloce, scorrevole e leggero scritto in GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:190 msgid "translator-credits" msgstr "Albano Battistella, Mek101" #: src/application.vala:573 msgid "Keep playing" msgstr "Continuare a riprodurre" #: src/ui/music-widgets.vala:232 src/ui/music-widgets.vala:260 msgid "Play at Next" msgstr "Riproduci il prossimo" #: src/ui/music-widgets.vala:233 src/ui/music-widgets.vala:259 msgid "Play" msgstr "Riproduci" #: src/ui/music-widgets.vala:236 src/ui/play-panel.vala:222 msgid "Show _Cover File" msgstr "Mostra file copertina" #: src/ui/music-widgets.vala:238 src/ui/play-panel.vala:224 msgid "_Export Cover" msgstr "Exporta Cover" #: src/ui/music-widgets.vala:266 src/ui/play-panel.vala:56 msgid "Search Title" msgstr "Cerca titolo" #: src/ui/music-widgets.vala:267 src/ui/play-panel.vala:54 msgid "Search Album" msgstr "Cerca album" #: src/ui/music-widgets.vala:268 src/ui/play-panel.vala:55 msgid "Search Artist" msgstr "Cerca artista" #: src/ui/music-widgets.vala:269 msgid "_Show Music File" msgstr "_Mostra file musicali" #: src/ui/play-bar.vala:75 msgid "Repeat Music" msgstr "Ripeti canzone" #: src/ui/play-bar.vala:85 msgid "Play Previous" msgstr "Riproduci precedente" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:91 msgid "Play/Pause" msgstr "Riproduci/Pausa" #: src/ui/play-bar.vala:98 msgid "Play Next" msgstr "Riproduci prossimo" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Trascina e rilascia i file delle canzoni qui,\n" "oppure cambia la posizione della libreria: " #: src/ui/preferences.vala:44 src/ui/preferences.vala:64 msgid "Never" msgstr "Mai" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Sempre" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Solo alla copertina" #: src/ui/preferences.vala:64 msgid "Track" msgstr "Traccia" #: src/ui/store-panel.vala:66 msgid "Playing" msgstr "Riprodurre" #: src/ui/store-panel.vala:72 msgid "Artists" msgstr "Artisti" #: src/ui/store-panel.vala:77 msgid "Albums" msgstr "Album" #: src/ui/volume-button.vala:32 msgid "Volume: " msgstr "Volume: " #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album sconosciuto" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista sconosciuto" #~ msgid "Appearance" #~ msgstr "Aspetto" #~ msgid "Music Location" #~ msgstr "Posizione della musica" #~ msgid "Experimental" #~ msgstr "Sperimentale" #~ msgid "By Date" #~ msgstr "Per data" #~ msgid "Loading..." #~ msgstr "Caricamento in corso..." ================================================ FILE: po/ja.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # FIRST AUTHOR , YEAR. # Gnuey56 , 2023. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-26 21:16+0800\n" "PO-Revision-Date: 2023-08-01 16:05+0900\n" "Last-Translator: Gnuey56 \n" "Language-Team: Japanese\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=1; plural=0\n" "X-Generator: Gtranslator 42.0\n" #: data/app.desktop.in:3 src/application.vala:225 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "ミュージックプレイヤー" #: data/app.desktop.in:5 msgid "Play music" msgstr "音楽を再生" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;音楽;サウンド;プレイヤー;メディア;" "オーディオ;プレイリスト;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "一般" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "ショートカットを表示" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "設定" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "終了" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "再生" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "再生/一時停止" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "次の曲を再生" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "前の曲を再生" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "プレイリスト" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "検索" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "プレイリストの並びを切り替え" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "ライブラリを再読み込み" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:290 msgid "Back" msgstr "戻る" #: src/gtk/preferences.ui:10 msgid "General" msgstr "一般" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "ダークテーマを使用" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "背景ぼかしモード" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "プレイリストをコンパクトにする" #: src/gtk/preferences.ui:44 src/application.vala:424 msgid "Keep playing after window closed" msgstr "ウィンドウが閉じても再生を続ける" #: src/gtk/preferences.ui:57 msgid "Library" msgstr "ライブラリ" #: src/gtk/preferences.ui:61 msgid "Load music from folder" msgstr "フォルダーから音源を読み込み" #: src/gtk/preferences.ui:73 msgid "Monitor local file changes" msgstr "ローカルファイルの変更を表示" #: src/gtk/preferences.ui:85 msgid "Load thumbnails for non-local files" msgstr "サムネイルを外部から読み込む" #: src/gtk/preferences.ui:86 msgid "May cause slowdowns and excess network usage" msgstr "動作が遅くなり、ネットワークを過剰に使用するかもしれません" #: src/gtk/preferences.ui:99 msgid "Playback" msgstr "再生" #: src/gtk/preferences.ui:103 msgid "Show audio peak level" msgstr "オーディオレベルを表示" #: src/gtk/preferences.ui:107 msgid "Display characters" msgstr "文字で表示" #: src/gtk/preferences.ui:122 msgid "Rotate album cover" msgstr "アルバムカバーを回転させる" #: src/gtk/preferences.ui:134 msgid "Enable gapless playback" msgstr "ギャップレス再生を有効化" #: src/gtk/preferences.ui:146 msgid "Normalize volume with ReplayGain" msgstr "ReplayGain で音量を調整" #: src/gtk/preferences.ui:152 msgid "Prefer audio sink" msgstr "オーディオシンクを設定" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "メインメニュー" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "並び替え" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "検索" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "ライブラリを再読み込み(_R)" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "設定(_P)" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "キーボードショートカット(_K)" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "G4Music について(_A)" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:64 msgid "Album" msgstr "アルバム" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "アーティスト" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "アーティスト/アルバム" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "タイトル" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "最近" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "シャッフル" #: src/action-handles.vala:188 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "GTK4 で書かれた、高速で、なめらかなUI、軽量なミュージックプレイヤー。" #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:190 msgid "translator-credits" msgstr "Gnuey56" #: src/application.vala:573 msgid "Keep playing" msgstr "再生を続ける" #: src/ui/music-widgets.vala:232 src/ui/music-widgets.vala:260 msgid "Play at Next" msgstr "次に再生" #: src/ui/music-widgets.vala:233 src/ui/music-widgets.vala:259 msgid "Play" msgstr "再生" #: src/ui/music-widgets.vala:236 src/ui/play-panel.vala:222 msgid "Show _Cover File" msgstr "カバーファイルを表示(_C)" #: src/ui/music-widgets.vala:238 src/ui/play-panel.vala:224 msgid "_Export Cover" msgstr "オーディオカバーをエクスポート(_E)" #: src/ui/music-widgets.vala:266 src/ui/play-panel.vala:56 msgid "Search Title" msgstr "タイトルを探す" #: src/ui/music-widgets.vala:267 src/ui/play-panel.vala:54 msgid "Search Album" msgstr "アルバムを探す" #: src/ui/music-widgets.vala:268 src/ui/play-panel.vala:55 msgid "Search Artist" msgstr "アーティストを探す" #: src/ui/music-widgets.vala:269 msgid "_Show Music File" msgstr "音楽ファイルを表示(_S)" #: src/ui/play-bar.vala:75 msgid "Repeat Music" msgstr "再生をリピート" #: src/ui/play-bar.vala:85 msgid "Play Previous" msgstr "前の曲を再生" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:91 msgid "Play/Pause" msgstr "再生/一時停止" #: src/ui/play-bar.vala:98 msgid "Play Next" msgstr "次の曲を再生" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "音源ファイルをここにドラッグアンドドロップ、\n" "または音源の場所を変更: " #: src/ui/preferences.vala:44 src/ui/preferences.vala:64 msgid "Never" msgstr "しない" #: src/ui/preferences.vala:44 msgid "Always" msgstr "常に" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "カバーがある場合のみ" #: src/ui/preferences.vala:64 msgid "Track" msgstr "トラック" #: src/ui/store-panel.vala:66 msgid "Playing" msgstr "再生" #: src/ui/store-panel.vala:72 msgid "Artists" msgstr "アーティスト" #: src/ui/store-panel.vala:77 msgid "Albums" msgstr "アルバム" #: src/ui/volume-button.vala:32 msgid "Volume: " msgstr "音量: " #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "不明のアルバム" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "不明のアーティスト" #~ msgid "Music Location" #~ msgstr "音源の場所" #~ msgid "Loading…" #~ msgstr "読み込み中…" ================================================ FILE: po/ka.po ================================================ # Georgian translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Ekaterine Papava , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-18 06:28+0200\n" "Last-Translator: Ekaterine Papava \n" "Language-Team: Georgian \n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "პაუზის გარეშე" #: data/app.desktop.in:4 msgid "Music Player" msgstr "მედია დამკვრელი" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "დაუკარით თქვენი მუსიკა ელეგანტურად" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "მახასიათებლები" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "" #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "აუდიოს პიკების დონის ჩვენება." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "პაუზის-გარეშე დაკვრის ჩართვა." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "ხმის ნორმალიზაცია ReplayGain-ით." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "" #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "" #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "მთავარი ფანჯარა" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "ალბომების ხედი" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "დაკვრის ხედი" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "დასაკრავი სიის ხედი" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "ზოგადი" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "მალსახმობების ჩვენება" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "მორგება" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "გასვლა" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "ჭდეების ჩვენება" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "დაკვრა" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "დაკვრა/პაუზა" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "შემდეგის დაკვრა" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "წინას დაკვრა" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "დასაკრავი სია" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "ძებნა" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "დალაგების გადართვა" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "ბიბლიოთეკის თავიდან ჩატვირთვა" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "სიის შენახვა" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "უკან" #: src/gtk/preferences.ui:10 msgid "General" msgstr "ზოგადი" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "ფონის გაბუნდოვნების რეჟიმი" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "კომპაქტური დასაკრავი სიის რეჟიმი" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "ბადის ხედი შემსრულებლებისთვის/ალბომებისთვის" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "დაკვრის გაგრძელება ფანჯრის დახურვის შემდეგ" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "ბიბლიოთეკა" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "მუსიკის ჩატვირთვა საქაღალდიდან" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "ლოკალური ფაილის ცვლილებების თვალყურის დევნება" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "მინიატურების ჩატვირთვა არალოკალური ფაილებისთვის" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "შეიძლება, გამოიწვიოს შენელება და გადამეტებული ქსელის დატვირთვა" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "დაკვრა" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "აუდიოს პიკების დონის ჩვენება" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "სიმბოლოების ჩვენება" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "ალბომის ყდის შებრუნება" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "პაუზის-გარეშე დაკვრის ჩართვა" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "ხმის ნორმალიზაცია ReplayGain-ით" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "GStreamer-ის აუდიო კოდეკის გამოყენება" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "მთავარი მენიუ" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "ძებნა" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "დახარისხება" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_ფერების სქემა" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "სისტემური" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_ღია" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_მუქი" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "ბიბლიოთეკის თავიდან ჩატვი_რთვა" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_ერთზე მეტის არჩევა" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_სიის შენახვა" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "გამართვა" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_კლავიატურის მალსახმობები" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "შესახებ" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "ალბომი" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "შემსრულებელი" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "შემსრულებელი/ალბომი" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "სათაური" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Ბოლო" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "შემთხვევით" #: src/action-handles.vala:114 msgid "Image Files" msgstr "გამოსახულების ფაილები" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "ყდის გატანა წარმატებულია" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "ყდის გატანა ჩავარდა" #: src/application.vala:350 msgid "Save playlist failed" msgstr "დასაკრავი სიის შენახვა ჩავარდა" #: src/application.vala:447 msgid "Playlist Files" msgstr "დასაკრავი სიის ფაილები" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "დასაკრავი სიის შენახვა წარმატებულია" #: src/application.vala:597 msgid "Keep playing" msgstr "დაკვრის გაგრძელება" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "სწრაფი, წმინდა, მსუბუქი მუსიკმკვრელი GTK4-ში." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "თარგმანი-შესრულებულია" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "არა" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "კი" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "დასაკრავი სია შეცვლილია, შეინახავთ?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "შემდეგზე დაკვრა" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "_რიგში ჩამატება" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "_დასაკრავი სიაში დამატება…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_წაშლა" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_სანაგვე ყუთში გადატანა" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "ყველას მონიშვნა" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "შემდეგზე დაკვრა" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "რიგში ჩამატება" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "დასაკრავი სიაში დამატება" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "წაშლა" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "თამაში" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "შემთხვევითი დაკვ_რა" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "სიის _ფაილის ჩვენება" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "სათაურის ძებნა" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "ალბომის ძებნა" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "შემსრულებლის ძებნა" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "ყდის _ფაილის ჩვენება" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "ყდის _ფაილის გატანა" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "ჭ_დეების ჩვენება…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "მ_უსიკის ფაილის ჩვენება" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "ერთხელ გამეორება" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "წინას დაკვრა" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "დაკვრა/პაუზა" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "შემდეგის დაკვრა" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "გადმოათრიეთ და დაყარეთ მუსიკის ფაილები აქ,\n" "ან შეცვალეთ მუსიკის მდებარეობა: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "ახალი დასაკრავი სია" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "არასდროს" #: src/ui/preferences.vala:44 msgid "Always" msgstr "ყოველთვის" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "მხოლოდ ყდა" #: src/ui/preferences.vala:66 msgid "Track" msgstr "ტრეკი" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "მიმდინარეობს დაკვრა" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "შემსრულებლები" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "ალბომები" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "დასაკრავი სიები" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "კოპირება" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "ხმის სიმძლავრე %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "ჩვენება" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "უცნობი ალბომი" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "უცნობი შემსრულებელი" #~ msgid "Play music" #~ msgstr "მუსიკის დაკვრა" #~ msgid "Prefer dark theme" #~ msgstr "მუქი თემის არჩევანი" #~ msgid "G4Music" #~ msgstr "G4Music" ================================================ FILE: po/meson.build ================================================ i18n.gettext(meson.project_name(), preset: 'glib') ================================================ FILE: po/nl.po ================================================ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Heimen Stoffels , 2022. # Nathan Follens , 2024. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-30 18:08+0100\n" "Last-Translator: Nathan Follens \n" "Language-Team: GNOME-NL https://matrix.to/#/#nl:gnome.org\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: Poedit 3.4.4\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Muziekspeler" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Speel uw muziek op elegante wijze af" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;muziek;geluid;speler;muziekspeler;" "speellijst;afspeellijst;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (AKA: G4Music) is een lichte muziekspeler, gemaakt in GTK4, gericht " "op grote muziekverzamelingen." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Functies" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Ondersteunt de meeste muziekbestandstypes, Samba en andere " "netwerkprotocollen (afhankelijk van GIO en GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Laadt snel en kan duizenden muziekbestanden verwerken in luttele seconden, " "controleert op lokale wijzigingen." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Heeft een laag geheugengebruik voor grote muziekverzamelingen met " "albumhoezen (ingebed en extern), zonder miniatuurcaches bij te houden." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Kan groeperen en sorteren op album/artiest/titel, willekeurig afspelen en " "zoeken in de volledige tekst." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Heeft een vlotte, adaptieve gebruikersinterface voor verschillende " "schermgroottes (desktop, tablet, mobiel)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Heeft een gaussiaans vervaagde hoes als achtergrond, volgt de lichte/" "donkere modus van GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Ondersteunt het aanmaken en bewerken van afspeellijsten, versleep de hoes " "om de volgorde te wijzigen of aan een andere afspeellijst toe te voegen." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Ondersteunt inslepen uit andere toepassingen." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Ondersteunt visualisatie van het piekvolume." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Ondersteunt ononderbroken afspelen." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Ondersteunt ReplayGain om het volume te normaliseren." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Ondersteunt de opgegeven audiosink." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Ondersteunt MPRIS-besturing." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Hoofdvenster" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albumweergave" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Afspeelweergave" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Afspeellijstweergave" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Algemeen" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Sneltoetsen tonen" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Voorkeuren" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Afsluiten" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Tags tonen" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Afspelen" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Afspelen/pauzeren" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Volgend nummer afspelen" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Vorig nummer afspelen" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Afspeellijst" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Zoeken" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Tussen sorteervolgordes schakelen" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Bibliotheek herladen" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Lijst opslaan" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Terug" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Algemeen" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Achtergrond vervagen" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Compacte afspeellijstweergave" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Rasterweergave voor artiesten/albums" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Eén keer klikken om item te activeren" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Doorgaan met afspelen wanneer het venster gesloten is" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotheek" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Muziek laden uit map" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Controleren op lokale wijzigingen aan bestanden" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Miniaturen van externe bestanden laden" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Let op: dit kan vertragingen en bovenmatig netwerkgebruik veroorzaken" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Afspelen" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Piekvolume tonen" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Gebruik deze tekens" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Albumhoes ronddraaien" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Ononderbroken afspelen" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "ReplayGain gebruiken om volume te normaliseren" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Voorkeur voor audiosink van GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Hoofdmenu" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Zoeken" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Sorteren op" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Kleurenschema" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_Systeem" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Licht" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Donker" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "Bibliotheek he_rladen" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Meerdere selecteren" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "Lijst op_slaan" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Voorkeuren" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "Snel_toetsen" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Over" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Artiest" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Artiest/album" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Titel" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Recent" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Willekeurig" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Afbeeldingsbestanden" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Albumhoes geëxporteerd" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Exporteren van hoes mislukt" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Opslaan van afspeellijst mislukt" #: src/application.vala:447 msgid "Playlist Files" msgstr "Afspeellijstbestanden" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Afspeellijst opgeslagen" #: src/application.vala:597 msgid "Keep playing" msgstr "Blijven afspelen" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Een eenvoudige, lichte en snelle muziekspeler, gemaakt met GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Heimen Stoffels \n" "Nathan Follens \n" "Meer info over GNOME-NL http://nl.gnome.org" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Nee" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Ja" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "De afspeellijst is bewerkt, wilt u deze opslaan?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Afspelen na huidige" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Toevoegen aan _wachtrij" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Toevoegen aan afs_peellijst…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "Ve_rwijderen" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Verplaatsen naar _prullenbak" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Alles selecteren" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Afspelen na huidige" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Toevoegen aan wachtrij" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Toevoegen aan afspeellijst" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Verwijderen" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Afspelen" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "Willekeu_rig afspelen" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Afspeellijst_bestand tonen" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Titel zoeken" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Album zoeken" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Artiest zoeken" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Hoes_bestand tonen" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "Hoes _exporteren" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "_Tags tonen…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Muziek_bestand tonen" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "1 keer herhalen" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Vorig nummer afspelen" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Afspelen/pauzeren" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Volgend nummer afspelen" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Sleep bestanden hierheen\n" "of wijzig de muzieklocatie: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Nieuwe afspeellijst" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "Geen afspeellijst gevonden in %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nooit" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Altijd" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Enkel albumhoezen" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Nummer" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "U luistert naar" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Artiesten" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albums" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Afspeellijsten" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopiëren" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volume %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "Tonen" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Onbekend album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Onbekende artiest" #~ msgid "G4Music" #~ msgstr "G4Music" #~ msgid "Play music" #~ msgstr "Luister naar muziek" #~ msgid "Prefer dark theme" #~ msgstr "Donker thema gebruiken" #~ msgid "Repeat Music" #~ msgstr "Nummer herhalen" #~ msgid "Appearance" #~ msgstr "Vormgeving" #~ msgid "Music Location" #~ msgstr "Muzieklocatie" #~ msgid "Experimental" #~ msgstr "Experimenteel" #~ msgid "By Date" #~ msgstr "Op datum" #~ msgid "Loading..." #~ msgstr "Bezig met laden…" ================================================ FILE: po/oc.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-02 21:22+0000\n" "PO-Revision-Date: 2024-11-15 20:00+0100\n" "Last-Translator: Quentin PAGÈS\n" "Language-Team: \n" "Language: oc\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.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:271 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Lector de musica" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Legissètz vòstra music amb elegància" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musica;son;lector;mèdia;àudio;audio;lista de lectura;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (tanben conegut coma G4Music) es un lector de musica leugièr escrich " "en GTK4, se concentra sus las grandas colleccions musicalas." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Foncionalitats" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Pren en carga la màger part dels tipes de fichièrs musicals, Samba e quin " "autre protocòl a distància que siá (depend de GIO e GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Cargament rapid e analisi de milièrs de fichièrs musicals en fòrça paucas " "segondas, susvelhança dels cambiaments locals." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Utilizacion de memòria bassa per una granda colleccion de musica amb de " "coberturas d'albums (integradas e extèrnas), pas de caches de vinhetas " "d’emmagazinar." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Gropar e triar per album/artista/títol, lista de mescla, recèrca de tèxte " "complèt." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Interfàcia d'utilizaire adaptativa fluida per mantun ecran (burèu, tauleta, " "mobil)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Cobertura en fosc gaussian coma rèireplan, seguís lo mòde clar/escur GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Pren en carga la creacion e modificacion de listas de lectura, lisar " "depausar per cambiar l’òrdre o apondre a una autra lista de lectura." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Pren en carga del lisar depausar d’autras aplicacions." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Permet d’afichar lo nivèl àudio de poncha." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Pren en carga la lectura sens interrupcion." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Compatible amb la normalizacion del volum amb ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Pren en carga un receptors àudio especificat." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Compatible amb los contraròtles MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Fenèstra principala" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Vista albums" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Vista lectura" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Vista lista de lectura" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "General" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Afichar los acorchis" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferéncias" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Sortir" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Afichar las etiquetas" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Lectura" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Lectura/Pausa" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Legir la seguenta" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Legir la precedenta" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Lista de lectura" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Cercar" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Alternar sens de triada" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Recargar la lista" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Enregistrar la lista" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:633 #: src/ui/store-panel.vala:423 msgid "Back" msgstr "Tornar" #: src/gtk/preferences.ui:10 msgid "General" msgstr "General" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Mòde del rèireplan fosc" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Vista compacta lista de lectura" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Vista grasilha per artista/albums" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Clic simple per activar l’element" #: src/gtk/preferences.ui:56 src/application.vala:430 msgid "Keep playing after window closed" msgstr "Téner de legir un còp la fenèstra tampada" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotèca" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Cargar la musica a partir del dossièr" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Susvelhar las modificacions dels fichièrs locals" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Cargar las miniaturas dels fichièrs non locals" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Pòt alentir e sollicitar mai la ret" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Lectura" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Afichar lo nivèl àudio de poncha" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Afichar los caractèrs" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Pivotar la cobertura dels album" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Activa la lectura sens interrupcion" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalizar lo volum amb ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Preferir lo contenedor àudio de Gstreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menú principal" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Cercar" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Triar per" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "Esquèma de _colors" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_Sistèma" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Clar" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Escur" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "_Recargar la bibliotèca" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Seleccion multipla" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Enregistrar la lista" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Preferéncias" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Acorchis de clavièr" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_A prepaus" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Artista/album" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Títol" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Recents" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Aleatòri" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Fichièrs imatges" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Succès de l’expòrt de la pocheta" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Fracàs de l’expòrt de la pocheta" #: src/application.vala:344 msgid "Save playlist failed" msgstr "Fracàs de l’expòrt de la lista de lectura" #: src/application.vala:466 msgid "Playlist Files" msgstr "Fichièrs de lista de lectura" #: src/application.vala:480 msgid "Save playlist successfully" msgstr "Succès de l’enregistrament de la lista de lectura" #: src/application.vala:615 msgid "Keep playing" msgstr "Contunhar de legir" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Un lector de musica rapid e leugièr esrich en GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Quentin PAGÈS" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "Non" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "Òc" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "Lista de lectura modificada, l’enregistrar ?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:406 src/ui/music-list.vala:422 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Legir a la _seguida" #: src/ui/music-list.vala:408 src/ui/music-list.vala:424 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "_Apondre a la lista d'espèra" #: src/ui/music-list.vala:409 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "_Apondre a la lista de lectura…" #: src/ui/music-list.vala:410 src/ui/music-list.vala:428 msgid "_Remove" msgstr "_Levar" #: src/ui/music-list.vala:429 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Metre a l'escobilhièr" #: src/ui/music-list.vala:637 msgid "Select All" msgstr "Tot seleccionar" #: src/ui/music-list.vala:654 msgid "Play at Next" msgstr "Legir a la seguida" #: src/ui/music-list.vala:661 msgid "Add to Queue" msgstr "Apondre a la lista d'espèra" #: src/ui/music-list.vala:668 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Apondre a la lista de lectura" #: src/ui/music-list.vala:675 msgid "Remove" msgstr "Levar" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:431 msgid "Play" msgstr "Lectura" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "Lectura _aleatòria" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Afichar fichièr de lista" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Recercar de títols" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Recercar d’albums" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Recercar d’artistas" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "_Afichar fichièr de cobertura" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Exportar la pocheta" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Afichar las _etiquetas…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "_Afichar fichièr musical" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Bocla simpla" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Legir la precedenta" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Lectura/Pausa" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Legir la seguenta" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Lisatz depausatz los fichièrs musicals aicí,\n" "o modificatz l’emplaçament de la musica : " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Lista de lectura novèla" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:702 #, c-format msgid "No playlist found in %s" msgstr "Cap de lista de lectura pas trobada dins %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Pas jamai" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Totjorn" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Pocheta de l'album sonque" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Pista" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "En lectura" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Artistas" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albums" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Listas de lectura" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Copiar" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volum %d%%" #: src/ui/window.vala:119 msgid "Show" msgstr "Mostrar" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album desconegut" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista desconegut" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "" #~ "Lisar-depausar de GNOME Fichièrs estant, afichatge de la musica dins " #~ "Fichièrs." #~ msgid "Play music" #~ msgstr "Legir de fichièrs àudio" #~ msgid "Prefer dark theme" #~ msgstr "Preferir lo tèma escur" #~ msgid "G4Music" #~ msgstr "G4Music" #~ msgid "Repeat Music" #~ msgstr "Repetir la cançon" #~ msgid "Appearance" #~ msgstr "Aparéncia" #~ msgid "Music Location" #~ msgstr "Emplaçament de la musica" #~ msgid "Experimental" #~ msgstr "Experimental" #~ msgid "By Date" #~ msgstr "Per data" #~ msgid "Loading..." #~ msgstr "Cargament…" ================================================ FILE: po/pt_BR.po ================================================ # Brazilian Portuguese translation for g4music. # Copyright (C) 2022-2024 g4music'S COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Pedro Henrique da Silva , 2023. # Fúlvio Leo , 2024. # Rafael Fontenelle , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-13 00:14-0300\n" "Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \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: Gtranslator 47.0\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Reprodutor de música" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Reproduza suas músicas de forma elegante" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "música;som;player;reprodutor;mídia;audio;playlist;lista de reprodução;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (também conhecido como G4Music) é um reprodutor de música leve " "escrito em GTK4, focado em grandes coleções de músicas." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Recursos" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Suporte à maioria dos tipos de arquivos de música, Samba e quaisquer outros " "protocolos remotos (depende do GIO e do GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Carregamento rápido e análise de milhares de arquivos de música em poucos " "segundos, monitora alterações locais." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Baixo uso de memória para grandes coleções de músicas com capas de álbuns " "(incorporadas e externas), sem caches de miniaturas para armazenar." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Agrupa e classifica por álbum/artista/título, lista aleatória, pesquisa de " "texto completo." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Interface de usuário adaptável e fluida para diferentes telas (desktop, " "tablet, celular)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Capa desfocada gaussiana como plano de fundo, segue o modo claro/escuro do " "GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Suporta criação e edição de listas de reprodução, arraste a capa para " "alterar a ordem ou adicionar a outra lista de reprodução." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Suporte a arrastar e soltar com outros aplicativos." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Suporte a um visualizador de picos de áudio." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Suporte à reprodução sem intervalos." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Suporte a normalização de volume com ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Suporte a sink de áudio especificado." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Suporte a controle MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Menu principal" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Visão dos álbuns" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Visão da reprodução" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Visão da lista de Reprodução" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Geral" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Mostra atalhos" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferências" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Sai" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Mostra tags" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Reprodução" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Reproduz/Pausa" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Próxima música" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Música anterior" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Lista de reprodução" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Procura" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Alterna a classificação" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Recarrega a biblioteca" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Salvar lista" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Voltar" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Geral" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Modo de desfoque de fundo" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Visualização compacta da lista de reprodução" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Visualização em grade para artistas/álbuns" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Clique único para ativar item" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Continuar reproduzindo após fechar a janela" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Biblioteca" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Carregar músicas da pasta" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Monitorar mudanças nos arquivos locais" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Carregar miniaturas para arquivos não-locais" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Pode causar lentidão e uso excessivo da rede" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Reprodução" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Mostrar níveis de pico do áudio" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Exibir caracteres" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Girar capa do álbum" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Permitir reprodução sem intervalos" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalizar voluma com ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Preferência de receptor de áudio do GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Menu principal" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Procurar" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Ordenar por" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "Esquema de _cor" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_Sistema" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Claro" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Escuro" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "_Recarregar biblioteca" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Múltipla seleção" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Salvar lista" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Preferências" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Atalhos de teclado" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Sobre" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Álbum" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Artista" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Artista/Álbum" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Título" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Recente" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Aleatório" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Arquivos de imagem" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Capa exportada com sucesso" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Falha ao exportar a capa" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Falha ao salvar a lista de reprodução" #: src/application.vala:447 msgid "Playlist Files" msgstr "Arquivos de lista de reprodução" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Lista de reprodução salvada com sucesso" #: src/application.vala:597 msgid "Keep playing" msgstr "Continuar tocando" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Um reprodutor de música leve, rápido e fluido escrito em GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Pedro Henrique da Silva \n" "Fúlvio Leo \n" "Rafael Fontenelle " #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Não" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Sim" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "A lista de reprodução foi modificada, deseja salvá-la?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Reproduzir na _próxima" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Adicionar à _fila" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Adicionar à _lista de reprodução…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Remover" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Mover para lixeira" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Selecionar tudo" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Reproduzir na próxima" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Adicionar à fila" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Adicionar à lista de Reprodução" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Remover" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Reproduzir" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Reprodução aleatória" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Mostrar _arquivo de lista" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Procurar título" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Procurar álbum" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Procurar artista" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Mostrar _arquivo de capa" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Exportar capa" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Mostrar _tags…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Mostrar _arquivo de música" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Loop único" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Reproduzir anterior" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Reproduzir/Pausar" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Reproduzir próxima" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Arraste e solte arquivos de música aqui,\n" "ou mude o local das músicas: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Nova lista de reprodução" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "Nenhuma lista de reprodução encontrada em %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nunca" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Sempre" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Apenas arte" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Faixa" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Tocando" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Artistas" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Álbuns" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Listas de reprodução" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Copiar" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volume %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "Mostrar" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Álbum desconhecido" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artista desconhecido" #~ msgid "Play music" #~ msgstr "Reproduzir música" #~ msgid "Prefer dark theme" #~ msgstr "Preferir tema escuro" #~ msgid "G4Music" #~ msgstr "G4Music" #~ msgid "Repeat Music" #~ msgstr "Repetir Música" #~ msgid "Appearance" #~ msgstr "Aparência" #~ msgid "Music Location" #~ msgstr "Localização das Músicas" #~ msgid "Experimental" #~ msgstr "Experimental" #~ msgid "Loading..." #~ msgstr "Carregando..." ================================================ FILE: po/ro.po ================================================ # Romanian translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Florentina Mușat , 2023. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/-/issues\n" "POT-Creation-Date: 2023-10-17 17:12+0000\n" "PO-Revision-Date: 2023-10-25 13:16+0300\n" "Last-Translator: Florentina Mușat \n" "Language-Team: Romanian \n" "Language: ro\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==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);;\n" "X-Generator: Poedit 3.4\n" #: data/app.desktop.in:3 src/application.vala:235 src/main.vala:24 msgid "G4Music" msgstr "G4Music" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Player de muzică" #: data/app.desktop.in:5 msgid "Play music" msgstr "Redă muzică" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "music;sound;player;media;audio;playlist;muzică;sunet;listă de redare;" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Generale" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Arată scurtăturile" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Preferințe" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Ieșire" #: src/gtk/help-overlay.ui:34 msgctxt "shortcut window" msgid "Playback" msgstr "Redare" #: src/gtk/help-overlay.ui:37 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Redare/pauză" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play Next" msgstr "Redă următoarea" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Previous" msgstr "Redă anterioara" #: src/gtk/help-overlay.ui:57 msgctxt "shortcut window" msgid "Playlist" msgstr "Listă de redare" #: src/gtk/help-overlay.ui:60 msgctxt "shortcut window" msgid "Search" msgstr "Caută" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Comută sortarea" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Reload Library" msgstr "Reîncarcă biblioteca" #: src/gtk/play-panel.ui:16 src/ui/store-panel.vala:340 msgid "Back" msgstr "Înapoi" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Generale" #: src/gtk/preferences.ui:14 msgid "Prefer dark theme" msgstr "Preferă tema întunecată" #: src/gtk/preferences.ui:26 msgid "Background blur mode" msgstr "Mod de estompare a fundalului" #: src/gtk/preferences.ui:32 msgid "Compact playlist view" msgstr "Vizualizare compactă a listei de redare" #: src/gtk/preferences.ui:44 msgid "Grid view for artists/albums" msgstr "Vizualizare grilă pentru artiști/albume" #: src/gtk/preferences.ui:56 src/application.vala:435 msgid "Keep playing after window closed" msgstr "Continuă redarea după ce s-a închis fereastra" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotecă" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Încarcă muzică din dosar" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Monitorizează modificările fișierelor locale" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Încarcă miniaturile pentru fișierele nelocale" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Poate cauza încetiniri și utilizarea excesivă a rețelei" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Redare" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Arată nivelul de vârf audio" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Afișează caracterele" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Rotește coperta albumului" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Activează redarea fără întrerupere" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalizează volumul cu ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Preferă receptorul audio al GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Meniu principal" #: src/gtk/store-panel.ui:28 msgid "Sort By" msgstr "Sortează după" #: src/gtk/store-panel.ui:35 msgid "Search" msgstr "Caută" #: src/gtk/store-panel.ui:73 msgid "_Reload Library" msgstr "_Reîncarcă biblioteca" #: src/gtk/store-panel.ui:79 msgid "_Preferences" msgstr "_Preferințe" #: src/gtk/store-panel.ui:83 msgid "_Keyboard Shortcuts" msgstr "Scurtături de _tastatură" #: src/gtk/store-panel.ui:87 msgid "_About" msgstr "_Despre" #: src/gtk/store-panel.ui:96 src/ui/preferences.vala:67 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:101 msgid "Artist" msgstr "Artist" #: src/gtk/store-panel.ui:106 msgid "Artist/Album" msgstr "Artist/album" #: src/gtk/store-panel.ui:111 msgid "Title" msgstr "Titlu" #: src/gtk/store-panel.ui:116 msgid "Recent" msgstr "Recente" #: src/gtk/store-panel.ui:121 msgid "Shuffle" msgstr "Amestecă" #: src/action-handles.vala:184 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Un player de muzică rapid, fluent și ușor scris în GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/action-handles.vala:186 msgid "translator-credits" msgstr "Florentina Mușat , 2023" #: src/application.vala:626 msgid "Keep playing" msgstr "Continuă redarea" #. Translators: Play this music at next position of current playing music #: src/ui/music-widgets.vala:252 src/ui/music-widgets.vala:293 #: src/ui/music-widgets.vala:303 msgid "Play at Next" msgstr "Redă la următoarea" #: src/ui/music-widgets.vala:253 src/ui/music-widgets.vala:292 #: src/ui/music-widgets.vala:302 src/ui/store-panel.vala:345 msgid "Play" msgstr "Redare" #: src/ui/music-widgets.vala:256 src/ui/play-panel.vala:237 msgid "Show _Cover File" msgstr "Arată fișierul de _copertă" #: src/ui/music-widgets.vala:258 src/ui/play-panel.vala:239 msgid "_Export Cover" msgstr "_Exportă coperta" #: src/ui/music-widgets.vala:295 msgid "Show List File" msgstr "Arată fișierul de listă" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:56 msgid "Search Title" msgstr "Caută titlu" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:54 msgid "Search Album" msgstr "Caută album" #: src/ui/music-widgets.vala:311 src/ui/play-panel.vala:55 msgid "Search Artist" msgstr "Caută artist" #: src/ui/music-widgets.vala:312 msgid "_Show Music File" msgstr "_Arată fișierul de muzică" #. Translators: single loop the current music #: src/ui/play-bar.vala:74 msgid "Single Loop" msgstr "O singură buclă" #: src/ui/play-bar.vala:84 msgid "Play Previous" msgstr "Redă anterioara" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:90 msgid "Play/Pause" msgstr "Redare/pauză" #: src/ui/play-bar.vala:97 msgid "Play Next" msgstr "Redă următoarea" #: src/ui/play-panel.vala:280 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Trageți și plasați fișiere de muzică aici,\n" "sau modificați locația muzicii: " #: src/ui/preferences.vala:46 src/ui/preferences.vala:67 msgid "Never" msgstr "Niciodată" #: src/ui/preferences.vala:46 msgid "Always" msgstr "Întotdeauna" #: src/ui/preferences.vala:46 msgid "Art Only" msgstr "Doar arta" #: src/ui/preferences.vala:67 msgid "Track" msgstr "Pistă" #: src/ui/store-panel.vala:74 msgid "Playing" msgstr "Redare" #: src/ui/store-panel.vala:79 msgid "Artists" msgstr "Artiști" #: src/ui/store-panel.vala:84 msgid "Albums" msgstr "Albume" #: src/ui/store-panel.vala:374 msgid "Playlists" msgstr "Liste de redare" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volum %d%%" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Album necunoscut" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Artist necunoscut" ================================================ FILE: po/ru.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # David Lapshin , 2022. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 06:29+0000\n" "PO-Revision-Date: 2024-10-11 13:49+0300\n" "Last-Translator: Artur So \n" "Language-Team: \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:271 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Музыкальный проигрыватель" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Воспроизводите музыку элегантно" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;музыка;звук;проигрыватель;медиа;" "аудио;список;воспроизведения;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (AKA: G4Music) - это легкий музыкальный проигрыватель, написанный на " "GTK4, ориентированный на работу с большими музыкальными коллекциями." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Возможности" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Поддерживает большинство типов музыкальных файлов, Samba и любые другие " "удаленные протоколы (зависит от GIO и GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Быстрая загрузка и разбор тысяч музыкальных файлов за считанные секунды, " "отслеживание локальных изменений." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Низкое потребление памяти для большой музыкальной коллекции с обложками " "альбомов (встроенными и внешними), отсутствие кэша миниатюр для хранения." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Группировка и сортировка по альбомам/исполнителям/названиям, список в " "случайном порядке, полнотекстовый поиск." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Адаптивный пользовательский интерфейс для различных экранов (ПК, планшета, " "мобильного устройства)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Гауссово размытие обложки в качестве фона, соответствует светлому/темному " "режиму GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Поддерживает создание и редактирование списков воспроизведения, " "перетаскивание обложки для изменения порядка или добавления в другой список " "воспроизведения." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Поддерживает перетаскивание с других приложений." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Поддерживает визуализатор аудиопиков." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Поддерживает непрерывное воспроизведение." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Поддерживает нормализацию громкости с помощью ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Поддерживает указанный аудиопоток." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Поддерживает управление с помощью MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Главное окно" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Альбомы" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Играет сейчас" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Список воспроизведения" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Общие" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Комбинации клавиш" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Параметры" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Выйти" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Показать метки" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Воспроизведение" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Воспроизведение/Пауза" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Воспроизвести следующую" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Воспроизвести предыдущую" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Список воспроизведения" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Поиск" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Переключить сортировку" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Перезагрузить библиотеку" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Сохранить список" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:420 msgid "Back" msgstr "Назад" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Общие" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Режим размытия фона" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Компактный вид списка" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Вид сеткой для исполнителей/альбомов" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Активация элемента одним щелчком мыши" #: src/gtk/preferences.ui:56 src/application.vala:419 msgid "Keep playing after window closed" msgstr "Продолжать воспроизведение после закрытия окна" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Библиотека" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Загружать музыку из папки" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Следить за изменениями в локальных файлах" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Загружать миниатюры для нелокальных файлов" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Может вызвать замедление работы и чрезмерное использование сети" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Воспроизведение" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Показывать уровень звука" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Показывать символы" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Вращать обложку альбома" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Включить непрерывное воспроизведение" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Нормализовывать громкость с помощью ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Отдавать предпочтение приёмнику звука GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Главное меню" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Поиск" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Сортировать" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Цветовая схема" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "С_истемная" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Светлая" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Темная" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "П_ерезагрузить библиотеку" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Выбрать несколько" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Сохранить список" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Параметры" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Комбинации клавиш" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_О приложении" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Альбом" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Исполнитель" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Исполнитель/Альбом" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Название" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Недавние" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Перемешать" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Файлы изображения" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Обложка успешно экспортирована" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Экспортировать обложку не удалось" #: src/application.vala:344 msgid "Save playlist failed" msgstr "Сохранить список воспроизведения не удалось" #: src/application.vala:441 msgid "Playlist Files" msgstr "Файлы списка воспроизведения" #: src/application.vala:455 msgid "Save playlist successfully" msgstr "Список воспроизведения успешно сохранен" #: src/application.vala:591 msgid "Keep playing" msgstr "Продолжать воспроизведение" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "" "Быстрый, плавный и лёгкий музыкальный проигрыватель, написанный на GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Давид Лапшин , 2022" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Нет" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Да" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Список воспроизведения изменен, сохранить его?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Воспроизвести _следующую" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Добавить в _очередь" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Добавить в список _воспроизведения…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Удалить" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "О_тправить в корзину" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Выбрать все" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Воспроизвести следующую" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Добавить в очередь" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Добавить в список воспроизведения" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Удалить" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:428 msgid "Play" msgstr "Воспроизвести" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "Воспроизвести в _случайном порядке" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Показать список _файлов" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Поиск названия" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Поиск альбома" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Поиск исполнителя" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Показать ф_айл обложки" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Экспортировать обложку" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Показать _метки…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "_Показать музыкальный файл" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Зациклить повторение на одной" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Воспроизвести предыдущую" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Воспроизведение/Пауза" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Воспроизвести следующую" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Перетащите файлы песен сюда,\n" "или измените расположение музыки: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Новый список воспроизведения" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:694 #, c-format msgid "No playlist found in %s" msgstr "Список воспроизведения не найден в %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Никогда" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Всегда" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Только оформление" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Композиция" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Играет сейчас" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Исполнители" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Альбомы" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Списки воспроизведения" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Копировать" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Громкость %d%%" #: src/ui/window.vala:116 msgid "Show" msgstr "Показать" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Неизвестный альбом" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Неизвестный исполнитель" #~ msgid "Play music" #~ msgstr "Воспроизведение музыки" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Close" #~ msgstr "Закрыть" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "Перетаскивание из Файлов GNOME, отображение музыки в Файлах." #~ msgid "Prefer dark theme" #~ msgstr "Предпочтительна тёмная тема" #~ msgid "Appearance" #~ msgstr "Внешний вид" #~ msgid "Music Location" #~ msgstr "Расположение музыки" #~ msgid "Experimental" #~ msgstr "Экспериментальное" #~ msgid "By Date" #~ msgstr "По дате" #~ msgid "Loading..." #~ msgstr "Загрузка..." ================================================ FILE: po/sk.po ================================================ # Slovak translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Patrik Tapušík <2tapusik00patrik6@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-08 18:25+0000\n" "PO-Revision-Date: 2024-11-04 09:02+0100\n" "Last-Translator: Patrik Tapušík <2tapusik00patrik6@gmail.com>\n" "Language-Team: Slovak \n" "Language: sk\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) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Hudobný prehrávač" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Prehrávajte svoju hudbu elegantne" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "hudba;zvuk;prehrávač;multimédia;audio;zoznam;playlist;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (AKA: G4Music) je jednoduchý hudobný prehrávač napísaný v GTK4, " "ktorý sa orientuje na prehrávanie veľkých hudobných zbierok." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Vlastnosti" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Podporuje väčšinu hudobných formátov, SMB a akékoľvek iné vzdialené protokoly " "(závisí na GIO a GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Rýchle načítanie a spracovanie tisícok hudobných súborov v krátkom čase, " "sledovanie lokálnych zmien." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Nízka spotreba pamäte pri veľkých hudobných zbierkach s obalmi albumov (vnorené a " "externé), žiadne náhľady na ukladanie." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Zoskupuje a triedi podľa albumu/umelca/názvu, náhodné zamiešanie zoznamu, vyhľadávanie." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Plynulé adaptívne užívateľské rozhranie pre rôzne obrazovky (Desktop, Tablet, " "Telefón)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "Gaussovsky rozmazaný obal ako pozadie, aplikuje GNOME svetlý/tmavý režim." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Podporuje vytváranie a úpravu zoznamov, pretiahnite obal na zmenu poradia alebo " "pridajte do iného zoznamu." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Podporuje pretiahnutie a pustenie s inými aplikáciami." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Podporuje vizualizáciu zvukových vrcholov." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Podporuje gapless prehrávanie." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Podporuje normalizáciu hlasitosti s ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Podporuje špecifikovaný zvukový výstup." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Podporuje ovládanie MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Hlavné okno" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albumy" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Prehrávanie" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Zoznamy" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Všeobecné" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Zobraziť skratky" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Nastavenia" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Ukončiť" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Zobraziť štítky" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Prehrávanie" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Prehrať/Pozastaviť" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Ďalšia skladba" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Predchádzajúca skladba" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Zoznam" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Vyhľadať" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Zapnúť/vypnúť triedenie" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Znovu načítať knižnicu" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Uložiť zoznam" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:630 #: src/ui/store-panel.vala:425 msgid "Back" msgstr "Späť" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Všeobecné" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Režim rozmazania pozadia" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompaktné zobrazenie zoznamu" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Zobrazenie interpretov/albumov v mriežke" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Aktivovať položku jedným kliknutím" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Pokračovať v prehrávaní po zatvorení okna" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Knižnica" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Načítať hudbu z priečinka" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Kontrolovať lokálne zmeny súborov" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Načítať náhľady pre nelokálne súbory" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Môže spôsobiť spomalenie a nadmerné využitie siete" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Prehrávanie" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Zobraziť úroveň zvukového vrcholu" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Znaky" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Rotovať obal albumu" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Povoliť gapless prehrávanie" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalizovať hlasitosť s ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Preferovať zvukový výstup GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Hlavná ponuka" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Vyhľadávať" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Zoradiť podľa" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Farebná schéma" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "_Systém" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Svetlý" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Tmavý" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "_Znova načítať knižnicu" #: src/gtk/store-panel.ui:93 msgid "_Select" msgstr "_Vybrať" #: src/gtk/store-panel.ui:99 msgid "_Preferences" msgstr "_Nastavenia" #: src/gtk/store-panel.ui:103 msgid "_Keyboard Shortcuts" msgstr "_Klávesové skratky" #: src/gtk/store-panel.ui:107 msgid "_About" msgstr "_O programe" #: src/gtk/store-panel.ui:116 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:121 msgid "Artist" msgstr "_Umelec" #: src/gtk/store-panel.ui:126 msgid "Artist/Album" msgstr "Umelec/_Album" #: src/gtk/store-panel.ui:131 msgid "Title" msgstr "Titulok" #: src/gtk/store-panel.ui:136 msgid "Recent" msgstr "Nedávne" #: src/gtk/store-panel.ui:141 msgid "Shuffle" msgstr "Náhodne" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Obrázky" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Export obalu bol úspešný" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Export obalu zlyhal" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Uloženie zoznamu zlyhalo" #: src/application.vala:447 msgid "Playlist Files" msgstr "Súbory zoznamu" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Uloženie zoznamu bolo úspešné" #: src/application.vala:597 msgid "Keep playing" msgstr "Pokračovať v prehrávaní" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Rýchly, plynulý, jednoduchý hudobný prehrávač napísaný v GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Patrik Tapušík" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Nie" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Áno" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Zoznam je upravený, uložiť?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:222 src/ui/music-widgets.vala:244 msgid "Play at _Next" msgstr "Prehrať _nasledujúcu" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:223 src/ui/music-widgets.vala:245 msgid "Add to _Queue" msgstr "Pridať do _poradia" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:224 #: src/ui/music-widgets.vala:246 src/ui/music-widgets.vala:265 msgid "Add to _Playlist…" msgstr "Pridať do _zoznamu…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Odstrániť" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:231 msgid "_Move to Trash" msgstr "_Presunúť do koša" #: src/ui/music-list.vala:634 msgid "Select All" msgstr "Označiť všetko" #: src/ui/music-list.vala:651 msgid "Play at Next" msgstr "Prehrať nasledujúcu" #: src/ui/music-list.vala:658 msgid "Add to Queue" msgstr "Pridať do poradia" #: src/ui/music-list.vala:665 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Pridať do zoznamu" #: src/ui/music-list.vala:672 msgid "Remove" msgstr "Odstrániť" #: src/ui/music-widgets.vala:219 src/ui/music-widgets.vala:241 #: src/ui/store-panel.vala:433 msgid "Play" msgstr "Prehrať" #: src/ui/music-widgets.vala:220 src/ui/music-widgets.vala:242 msgid "_Random Play" msgstr "_Prehrať náhodne" #: src/ui/music-widgets.vala:230 msgid "Show List _File" msgstr "Zobraziť zoznam _súborov" #: src/ui/music-widgets.vala:253 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Vyhľadať názov" #: src/ui/music-widgets.vala:254 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Vyhľadať album" #: src/ui/music-widgets.vala:255 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Vyhľadať umelca" #: src/ui/music-widgets.vala:259 msgid "Show Cover _File" msgstr "Zobraziť obal _súboru" #: src/ui/music-widgets.vala:261 msgid "_Export Cover" msgstr "_Exportovať obal" #: src/ui/music-widgets.vala:262 msgid "Show _Tags…" msgstr "Zobraziť _štítky…" #: src/ui/music-widgets.vala:263 msgid "Show Music _File" msgstr "Zobraziť _hudobný súbor" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Jedno opakovanie" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Prehrať predchádzajúcu" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Prehrať/Pozastaviť" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Prehrať nasledujúcu" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Sem pretiahnite a pustite hudobné súbory,\n" "alebo zmeňte umiestnenie hudby: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Nový zoznam" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:688 #, c-format msgid "No playlist found in %s" msgstr "Nebol nájdený žiadny zoznam v %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nikdy" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Vždy" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Iba obrázok" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Skladba" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Práve hrá" #: src/ui/store-panel.vala:88 msgid "Artists" msgstr "Interpreti" #: src/ui/store-panel.vala:92 msgid "Albums" msgstr "Albumy" #: src/ui/store-panel.vala:96 msgid "Playlists" msgstr "Zoznamy" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopírovať" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Hlasitosť %d%%" #: src/ui/window.vala:113 msgid "Show" msgstr "Zobraziť" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Neznámy album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Neznámy umelec" ================================================ FILE: po/sl.po ================================================ # Slovenian translation for g4music. # Copyright (C) 2024 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # Martin Srebotnjak , 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-11 14:31+0200\n" "Last-Translator: Martin Srebotnjak \n" "Language-Team: Slovenian \n" "Language: sl\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%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || " "n%100==4 ? 3 : 0);\n" "X-Generator: Poedit 2.2.1\n" "X-Poedit-SourceCharset: ISO-8859-1\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Brez vrzeli" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Predvajalnik glasbe" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Elegantno predvajajte glasbo" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "glasba;zvok;predvajalnik;medij;avdio;seznam predvajanja;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Brez vrzeli (AKA: G4Music) je lahek glasbeni predvajalnik, napisan v GTK4, " "ki se osredotoča na veliko glasbeno zbirko." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Zmožnosti" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Podpira večino vrst glasbenih datotek, Sambo in vse druge oddaljene " "protokole (odvisno od GIO in GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Hitro nalaganje in razčlenjevanje na tisoče glasbenih datotek v zelo nekaj " "sekundah, spremljajte krajevne spremembe." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Nizka poraba pomnilnika za veliko glasbeno zbirko z naslovnicami albumov " "(vdelanimi in zunanjimi), brez predpomnilnikov sličic za shranjevanje." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Združevanje in razvrščanje po albumu/izvajalcu/naslovu, naključni seznami, " "iskanju po celotnem besedilu." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Tekoč prilagodljiv uporabniški vmesnik za različne zaslone (namizje, " "tablični računalnik, mobilnik)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Gaussova zabrisana naslovnica kot ozadje, sledi svetlemu/temnemu načinu " "GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Podpira ustvarjanje in urejanje seznamov predvajanja, povlek naslovnice za " "spremembo vrstnega reda ali dodajanje na drug seznam predvajanja." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Podpira povleci in spusti z drugimi programi." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Podpira vizualizacijo najvišjih ravni zvoka." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Podpira predvajanje brez premorov." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Podpira normaliziranje glasnosti s funkcijo ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Podpira navedeni zvočni ponor." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Podpira nadzor MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Glavno okno" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Pogled albumov" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Predvajalni pogled" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Pogled seznama predvajanja" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Splošno" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Pokaže tipkovne bližnjice‫" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Nastavitve" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Izhod" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Pokaži značke" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Predvajanje" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Predvajaj / premor" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Predvajaj naslednje" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Predvajaj prejšnje" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Seznam predvajanja" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Iskanje" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Preklopi razvrščanje" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Znova naloži knjižnico" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Shrani seznam" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Nazaj" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Splošno" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Način zabrisanosti ozadja" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Strnjen pogled seznama predvajanja" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Mrežni pogled za izvajalce/albume" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "En klik za aktiviranje elementa" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Nadaljuj s predvajanjem po zaprtju okna" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Knjižnica" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Naloži glasbo iz mape" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Spremljaj krajevne spremembe datotek" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Naloži sličice za datoteke, ki niso krajevne" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Lahko povzroči upočasnitve in prekomerno porabo omrežja" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Predvajanje" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Pokaži najvišjo raven zvoka" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Prikaz znakov" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Zasukaj naslovnico albuma" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Omogoči predvajanje brez premorov" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normaliziraj glasnost s funkcijo ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Prednost naj ima zvočni odtok GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Glavni meni" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Iskanje" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Razvrsti po" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "_Barvna shema" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "S_istemska" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "_Svetla" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Temna" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "Znova naloži _knjižnico" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "_Večdelni izbor" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "_Shrani seznam" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Možnosti" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Tipkovne bližnjice" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_O programu" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Izvajalec" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Izvajalec/album" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Naslov" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Nedavno" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Premešaj" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Slikovne datoteke" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Izvoz ovitka je uspešno zaključen" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Izvoz ovitka je spodletel" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Shranjevanje seznama predvajanja je spodletelo" #: src/application.vala:447 msgid "Playlist Files" msgstr "Datoteke seznama predvajanja" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Seznam predvajanja uspešno shranjen" #: src/application.vala:597 msgid "Keep playing" msgstr "Nadaljuj s predvajanjem" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "Hiter, tekoč, lahek predvajalnik glasbe, napisan v GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Martin Srebotnjak " #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Ne" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Da" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "Seznam predvajanja je spremenjen, ga želite shraniti?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Predvajaj kot nas_lednje" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Dodaj v _čakalno vrsto" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Dodaj na seznam _predvajanja …" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "_Odstrani" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "_Premakni v smeti" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Izberi vse" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Predvajaj kot naslednje" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Dodaj v čakalno vrsto" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Dodaj na seznam predvajanja" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Odstrani" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Predvajaj" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Naključno predvajanje" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Pokaži _datoteko seznama" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Iskanje po naslovu" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Iskanje po albumu" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Iskanje po izvajalcu" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Pokaži datoteko _ovitka" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Izvozi ovitek" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Pokaži _značke ..." #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Pokaži _glasbeno datoteko" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Enojna zanka" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Predvajaj prejšnje" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Predvajaj / premor" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Predvajaj naslednje" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Povlecite in spustite glasbene datoteke semkaj\n" "ali spremenite mesto glasbe: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Nov seznam predvajanja" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "V %s ni mogoče najti seznama predvajanja" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Nikoli" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Vedno" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Samo grafika" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Skladba/posnetek" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Trenutno se predvaja" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Izvajalci" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albumi" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Seznami predvajanja" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopiraj" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Glasnost %d %%" #: src/ui/window.vala:112 msgid "Show" msgstr "Pokaži" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Neznani album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Neznani izvajalec" ================================================ FILE: po/sv.po ================================================ # Swedish translation for G4Music. # Copyright 2022-2024 G4Music's COPYRIGHT HOLDER # This file is distributed under the same license as the G4Music package. # Åke Engelbrektson , 2022. # Anders Jonsson , 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-10 04:13+0000\n" "PO-Revision-Date: 2024-11-10 13:22+0100\n" "Last-Translator: Anders Jonsson \n" "Language-Team: Swedish \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: Poedit 3.5\n" # Namnet på spelaren. #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:271 #: src/main.vala:24 msgid "Gapless" msgstr "Gapless" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Musikspelare" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Spela din musik elegant" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "musik;ljud;spelare;media;audio;spelningslista;spellista;" # TODO: collection**s** #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (även kallad G4Music) är en lättviktig musikspelare skriven i GTK4 " "som fokuserar på stora musiksamlingar." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Funktioner" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Stöder de flesta filtyper för musik, Samba och andra fjärrprotokoll (beror " "på GIO och GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Snabb inläsning och tolkning av tusentals musikfiler på några få sekunder, " "övervaka lokala ändringar." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Låg minnesanvändning för stora musiksamlingar med albumomslag (inbäddade och " "externa), inga miniatyrbildscachar att lagra." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Gruppering och sortering efter album/artist/titel, slumplista, " "fulltextsökning." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Helt adaptivt användargränssnitt för olika skärmar (skrivbord, platta, " "mobil)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "Gaussiskt oskärpt omslag som bakgrund, följer GNOMEs ljusa/mörka läge." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Stöder skapande och redigering av spellistor, dra omslag för att ändra " "ordning eller lägga till i en annan spellista." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Stöder dra-och-släpp med andra program." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Stöder visualisering av ljudtoppnivå." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Stöder mellanrumslös uppspelning." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Stöder normalisering av volym med ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Stöder specificerad ljudutgång." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Stöder MPRIS-kontroll." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Huvudfönster" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albumvy" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Uppspelningsvy" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Spellistevy" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Allmänt" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Visa kortkommandon" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Inställningar" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Avsluta" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Visa taggar" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Uppspelning" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Spela/Pausa" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Spela nästa" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Spela förra" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Spellista" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Sök" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Växla sortering" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Uppdatera bibliotek" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Spara lista" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:633 #: src/ui/store-panel.vala:423 msgid "Back" msgstr "Bakåt" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Allmänt" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Läge med bakgrundsoskärpa" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Kompakt spellistevy" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Rutnätsvy för artister/album" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Enkelklick för att aktivera objekt" #: src/gtk/preferences.ui:56 src/application.vala:430 msgid "Keep playing after window closed" msgstr "Fortsätt spela efter att fönstret har stängts" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Bibliotek" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Hämta musik från mapp" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Övervaka lokala filändringar" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Hämta miniatyrer för icke-lokala filer" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Kan orsaka nedsaktning och överdriven nätverksanvändning" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Uppspelning" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Visa ljudtoppnivå" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Visa tecken" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Rotera albumomslag" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Aktivera mellanrumslös uppspelning" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Normalisera volym med ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Föredra ljudutgång för GStreamer" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Huvudmeny" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Sök" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Sortera efter" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_Färgschema" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_System" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Ljust" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Mörkt" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "_Uppdatera bibliotek" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "_Välj flera" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_Spara lista" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Inställningar" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_Tangentbordsgenvägar" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_Om" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Album" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Artist" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Artist/Album" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Titel" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Senaste" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Blanda" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Bildfiler" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Omslag exporterades" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Omslagsexport misslyckades" #: src/application.vala:344 msgid "Save playlist failed" msgstr "Sparning av spellista misslyckades" #: src/application.vala:466 msgid "Playlist Files" msgstr "Spellistefiler" #: src/application.vala:480 msgid "Save playlist successfully" msgstr "Spellistan sparades" #: src/application.vala:615 msgid "Keep playing" msgstr "Fortsätt spela" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "En snabb lättflytande musikspelare skriven i GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Åke Engelbrektson" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "Nej" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "Ja" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "Spellistan är ändrad, spara den?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:406 src/ui/music-list.vala:422 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Spela _härnäst" #: src/ui/music-list.vala:408 src/ui/music-list.vala:424 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Lägg till i _kö" #: src/ui/music-list.vala:409 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Lägg till i _spellista…" #: src/ui/music-list.vala:410 src/ui/music-list.vala:428 msgid "_Remove" msgstr "_Ta bort" #: src/ui/music-list.vala:429 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Flytta till _papperskorgen" #: src/ui/music-list.vala:637 msgid "Select All" msgstr "Markera alla" #: src/ui/music-list.vala:654 msgid "Play at Next" msgstr "Spela härnäst" #: src/ui/music-list.vala:661 msgid "Add to Queue" msgstr "Lägg till i kö" #: src/ui/music-list.vala:668 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Lägg till i spellista" #: src/ui/music-list.vala:675 msgid "Remove" msgstr "Ta bort" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:431 msgid "Play" msgstr "Spela" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "S_pela slumpmässigt" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Visa list_fil" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Sök titel" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Sök album" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Sök artist" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Visa omslags_fil" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Exportera omslag" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Visa _taggar…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Visa musik_fil" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Upprepa en" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Spela förra" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Spela/Pausa" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Spela nästa" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Dra och släpp musikfiler här,\n" "eller ändra musikplats: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Ny spellista" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:702 #, c-format msgid "No playlist found in %s" msgstr "Ingen spellista hittades i %s" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Aldrig" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Alltid" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Endast omslag" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Spår" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Spelar" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Artister" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Album" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Spellistor" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopiera" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Volym %d%%" #: src/ui/window.vala:119 msgid "Show" msgstr "Visa" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Okänt album" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Okänd artist" #~ msgid "Play music" #~ msgstr "Spela musik" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Close" #~ msgstr "Stäng" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "Dra-och-släpp från GNOME Filer, visa musik i Filer." #~ msgid "Prefer dark theme" #~ msgstr "Föredra mörkt tema" #~ msgid "No playlist found in: %s" #~ msgstr "Ingen spellista hittades i: %s" #~ msgid "_Add to Playlist…" #~ msgstr "_Lägg till i spellista…" ================================================ FILE: po/tr.po ================================================ # Turkish translation for g4music. # Copyright (C) 2022-2024 g4music'S COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # # Sabri Ünal , 2022-2024. # Emin Tufan Çetin , 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-11-18 01:03+0000\n" "PO-Revision-Date: 2024-10-23 22:55+0300\n" "Last-Translator: Sabri Ünal \n" "Language-Team: Turkish \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=1; plural=0;\n" "X-Generator: Poedit 3.5\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:271 #: src/main.vala:24 msgid "Gapless" msgstr "Aralıksız" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Müzik Çalar" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Müziğinizi zarifçe çalın" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;müzik;ses;çalıcı;medya;ortam;çalma " "listesi;oynatma listesi;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (Yani: G4Music), GTK4 ile yazılmış hafif bir müzik oynatıcıdır ve " "büyük müzik derlemelerine odaklanır." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Özellikler" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Çoğu müzik dosyası türününün yanında Samba ve diğer uzak iletişim " "kurallarını destekler (GIO ve GStreamerʼa bağlıdır)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Binlerce müzik dosyasını birkaç saniyede yükleme ve ayrıştırma, yerel " "değişiklikleri izleme." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Albüm kapakları (gömülü ve harici) içeren büyük müzik derlemeleri için düşük " "bellek kullanımı, küçük resim önbellekleri depolanmaz." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Albüme/sanatçıya/başlığa göre gruplama ve sıralama, liste karıştırma, tam " "metin arama." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Farklı ekranlar (Masaüstü, Tablet, Cep Telefonu) için akıcı uyarlanabilir " "kullanıcı arayüzü." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "Arka plan için bulanıklaştırılmış kapak, GNOME açık/koyu kipini takip." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Oynatma listeleri oluşturmayı ve düzenlemeyi destekler. Sırayı değiştirmek " "ya da başka oynatma listesine eklemek için kapağı sürükleyin." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Diğer uygulamalarla sürükle ve bırak özelliğini destekler." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Ses tepe seviyesi görselleyiciyi destekler." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Aralıksız çalmayı destekler." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "ReplayGain ile sesi normalleştirmeyi destekler." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Belirtilen ses alıcılarını destekler." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "MPRIS denetimini destekler." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Ana pencere" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Albümler görünümü" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Çalıyor görünümü" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Çalma listesi görünümü" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Genel" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Kısayolları Göster" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Tercihler" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Çık" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Etiketleri Göster" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Çalma" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Çal/Duraklat" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Sonrakini Çal" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Öncekini Çal" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Çalma Listesi" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Ara" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Sıralamayı Aç/Kapat" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Kitaplığı Yeniden Yükle" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Listeyi Kaydet" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:632 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Geri" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Genel" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Arka plan bulanıklık kipi" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Sıkışık oynatma listesi görünümü" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Sanatçılar/albümler için ızgara görünümü" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Tek tıkla ögeyi etkinleştir" #: src/gtk/preferences.ui:56 src/application.vala:432 msgid "Keep playing after window closed" msgstr "Pencere kapatıldıktan sonra oynatmayı sürdür" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Kitaplık" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Müziği klasörden yükle" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Yerel dosya değişikliklerini izle" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Yerel olmayan dosyalar için küçük resimleri yükle" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Yavaşlığa ve aşırı ağ kullanımına neden olabilir" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Çalma" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Ses tepe seviyesini göster" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Karakterleri göster" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Albüm kapağını çevir" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Aralıksız oynatmayı etkinleştir" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Sesi normalleştirmek için ReplayGain kullan" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "GStreamer ses havuzunu yeğle" #: src/gtk/store-panel.ui:23 msgid "Main Menu" msgstr "Ana Menü" #: src/gtk/store-panel.ui:30 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Ara" #: src/gtk/store-panel.ui:40 msgid "Sort By" msgstr "Sıralama" #: src/gtk/store-panel.ui:71 msgid "_Color Scheme" msgstr "_Renk Şeması" #: src/gtk/store-panel.ui:73 msgid "_System" msgstr "_Sistem" #: src/gtk/store-panel.ui:78 msgid "_Light" msgstr "_Açık" #: src/gtk/store-panel.ui:83 msgid "_Dark" msgstr "_Koyu" #: src/gtk/store-panel.ui:90 msgid "_Reload Library" msgstr "Kitaplığı _Yeniden Yükle" #: src/gtk/store-panel.ui:94 msgid "_Multiple Select" msgstr "Ç_oklu Seçim" #: src/gtk/store-panel.ui:98 msgid "_Save List" msgstr "_Listeyi Kaydet" #: src/gtk/store-panel.ui:104 msgid "_Preferences" msgstr "_Tercihler" #: src/gtk/store-panel.ui:108 msgid "_Keyboard Shortcuts" msgstr "_Klavye Kısayolları" #: src/gtk/store-panel.ui:112 msgid "_About" msgstr "_Hakkında" #: src/gtk/store-panel.ui:121 src/ui/preferences.vala:66 msgid "Album" msgstr "Albüm" #: src/gtk/store-panel.ui:126 msgid "Artist" msgstr "Sanatçı" #: src/gtk/store-panel.ui:131 msgid "Artist/Album" msgstr "Sanatçı/Albüm" #: src/gtk/store-panel.ui:136 msgid "Title" msgstr "Başlık" #: src/gtk/store-panel.ui:141 msgid "Recent" msgstr "Son" #: src/gtk/store-panel.ui:146 msgid "Shuffle" msgstr "Karıştır" #: src/action-handles.vala:114 msgid "Image Files" msgstr "Görüntü Dosyaları" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Kapak dışa aktarıldı" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Kapak dışa aktarılamadı" #: src/application.vala:344 msgid "Save playlist failed" msgstr "Oynatma listesi kaydedilemedi" #: src/application.vala:468 msgid "Playlist Files" msgstr "Çalma Listesi Dosyaları" #: src/application.vala:483 msgid "Save playlist successfully" msgstr "Oynatma listesi kaydedildi" #: src/application.vala:618 msgid "Keep playing" msgstr "Oynamayı sürdür" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "GTK4 ile yazılmış hızlı, akıcı, hafif bir müzik çalar." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Sabri Ünal " #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:112 msgid "No" msgstr "Hayır" #: src/ui/dialogs.vala:101 src/ui/dialogs.vala:112 msgid "Yes" msgstr "Evet" #: src/ui/music-list.vala:276 msgid "Playlist is modified, save it?" msgstr "Oynatma listesi değiştirildi, kaydedilsin mi?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:405 src/ui/music-list.vala:421 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "_Sonra Çal" #: src/ui/music-list.vala:407 src/ui/music-list.vala:423 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "_Kuyruğa Ekle" #: src/ui/music-list.vala:408 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "_Oynatma Listesine Ekle…" #: src/ui/music-list.vala:409 src/ui/music-list.vala:427 msgid "_Remove" msgstr "_Kaldır" #: src/ui/music-list.vala:428 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "Çöpe _Taşı" #: src/ui/music-list.vala:636 msgid "Select All" msgstr "Tümünü Seç" #: src/ui/music-list.vala:653 msgid "Play at Next" msgstr "Sonra Çal" #: src/ui/music-list.vala:660 msgid "Add to Queue" msgstr "Kuyruğa Ekle" #: src/ui/music-list.vala:667 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Çalma Listesine Ekle" #: src/ui/music-list.vala:674 msgid "Remove" msgstr "Kaldır" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Çal" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Rastgele Oynat" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Liste _Dosyasını Göster" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Başlığı Göster" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Albüm Ara" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Sanatçı Ara" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Kapak _Dosyalasını Göster" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Kapağı Dışa Aktar" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "_Etiketleri Göster…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Müzik _Dosyasını Göster" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Tek Döngü" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Öncekini Çal" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Çal/Duraklat" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Sonrakini Çal" #: src/ui/play-panel.vala:263 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Şarkı dosyalarını sürükleyip buraya bırakın\n" "veya müzik konumunu değiştirin: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Yeni Oynatma Listesi" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:703 #, c-format msgid "No playlist found in %s" msgstr "%s içinde oynatma listesi bulunamadı" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Asla" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Daima" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Yalnızca Kapak" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Parça" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Çalıyor" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Sanatçılar" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Albümler" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Çalma Listesi" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Kopyala" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Ses %%%d" #: src/ui/window.vala:119 msgid "Show" msgstr "Göster" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Bilinmeyen Albüm" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Bilinmeyen Sanatçı" ================================================ FILE: po/uk.po ================================================ # Ukrainian translation for g4music. # Copyright (C) 2023 g4music's COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # # Yuri Chornoivan , 2023, 2024. msgid "" msgstr "" "Project-Id-Version: g4music master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/neithern/g4music/issues\n" "POT-Creation-Date: 2024-10-11 01:38+0000\n" "PO-Revision-Date: 2024-10-11 23:39+0300\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \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 23.04.3\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "Неперервний" #: data/app.desktop.in:4 msgid "Music Player" msgstr "Програвач музики" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "Елегантне відтворення вашої музики" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;музика;звук;програвач;мультимедіа;" "звук;список відтворення;відтворення;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "Gapless (або G4Music) — невибагливий до ресурсів засіб відтворення музики, " "який написано на GTK4 і акцент у якому зроблено на великих музичних збірках." #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "Можливості" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "Підтримка більшості типів музичних файлів, Samba та усіх інших протоколів " "віддаленої роботи (залежить від GIO і GStreamer)." #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "" "Швидке завантаження і обробка тисяч музичних файлів за декілька секунд, " "спостереження за змінами у локальному каталозі." #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "Незначне споживання пам'яті для великих музичних збірок із обкладинками " "альбомів (вбудованими або зовнішніми), у сховищі даних не кешуються " "мініатюри." #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "" "Групування та упорядкування за альбомом, виконавцем та назвою, перемішування " "списку, повнотекстовий пошук." #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "Гнучкий інтерфейс для різних розмірів екрана (робоча станція, планшет, " "мобільний телефон)." #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "" "Тло у вигляді обкладинки із гаусовим розмиванням, використання світлого або " "темного режиму GNOME." #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "" "Передбачено підтримку створення та редагування списків відтворення, " "перетягування за обкладинку для зміни порядку або додавання до іншого списку " "відтворення." #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "Підтримка перетягування зі скиданням з інших програм." #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "Підтримка візуалізації піків звукового сигналу." #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "Підтримка неперервного відтворення." #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "Підтримка нормалізації гучності за допомогою ReplayGain." #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "Підтримка вказаного приймача звукових даних." #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "Підтримка керування за допомогою MPRIS." #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "Головне вікно" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "Перегляд «Альбоми»" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "Перегляд «Відтворення»" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "Перегляд «Список відтворення»" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "Загальне" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "Показати клавіатурні скорочення" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "Налаштування" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "Вийти" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "Показувати мітки" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "Відтворення" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "Пуск/Пауза" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "Відтворити наступну" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "Відтворити попередній" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "Список відтворення" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "Пошук" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "Перемкнути упорядковування" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "Перезавантажити бібліотеку" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "Зберегти список" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "Назад" #: src/gtk/preferences.ui:10 msgid "General" msgstr "Загальне" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "Режим розмивання тла" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "Стислий перегляд списку" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "Таблиця для виконавців/альбомів" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "Активація одинарним клацанням" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "Продовжити відтворення після закриття вікна" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "Бібліотека" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "Завантажити музику з теки" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "Стежити за змінами у локальних файлах" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "Завантажувати мініатюри для нелокальних файлів" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "Може спричинити уповільнення роботи та зайве використання мережі" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "Відтворення" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "Показувати піковий рівень звуку" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "Показати символи" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "Обертати обкладинку альбому" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "Увімкнути неперервне відтворення" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "Нормалізувати гучність за допомогою ReplayGain" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "Перевага приймача звуку GStreamer" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "Головне меню" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "Пошук" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "Критерій впорядкування" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "Схема _кольорів" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "Загальнос_истемна" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "С_вітла" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "_Темна" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "П_ерезавантажити бібліотеку" #: src/gtk/store-panel.ui:93 #| msgid "_Select" msgid "_Multiple Select" msgstr "Вибрати д_екілька" #: src/gtk/store-panel.ui:97 #| msgctxt "shortcut window" #| msgid "Save List" msgid "_Save List" msgstr "З_берегти список" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "_Налаштування" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "_Клавіатурні скорочення" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "_Про програму" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "Альбом" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "Виконавець" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "Виконавець/Альбом" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "Назва" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "Нещодавні" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "Перемішати" #: src/action-handles.vala:114 msgid "Image Files" msgstr "файли зображень" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "Обкладинку успішно експортовано" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "Не вдалося експортувати обкладинку" #: src/application.vala:350 msgid "Save playlist failed" msgstr "Не вдалося зберегти список відтворення" #: src/application.vala:447 msgid "Playlist Files" msgstr "файли списків відтворення" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "Список відтворення успішно збережено" #: src/application.vala:597 msgid "Keep playing" msgstr "Продовжувати відтворення" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "" "Швидкий, плавний, невибагливий програвач музики, який засновано на GTK4." #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "Юрій Чорноіван , 2023" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "Ні" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "Так" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "До списку відтворення внесено зміни, зберегти їх?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "Відтворити _наступною" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "Додати до _черги відтворення" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "Додати до с_писку відтворення…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "В_илучити" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "П_ересунути до смітника" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "Позначити все" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "Відтворити наступною" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "Додати до черги відтворення" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "Додати до списку відтворення" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "Вилучити" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "Відтворити" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "_Випадкове відтворення" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "Показати _файл списку" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "Шукати за назвою" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "Шукати альбом" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "Шукати виконавця" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "Показати файл _обкладинки" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "_Експортувати обкладинку" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "Показати _мітки…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "Показати файл _музики" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "Одне повторення" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "Відтворити попередній" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "Пуск/Пауза" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "Відтворити наступну" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "Перетягніть і скиньте музику сюди\n" "або змініть розташування музики: " #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "Новий список" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "У %s не виявлено списку відтворення" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "Ніколи" #: src/ui/preferences.vala:44 msgid "Always" msgstr "Завжди" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "Лише оформлення" #: src/ui/preferences.vala:66 msgid "Track" msgstr "Композиція" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "Відтворення" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "Виконавці" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "Альбоми" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "Списки відтворення" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "Копіювати" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "Гучність %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "Показати" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "Невідомий альбом" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "Невідомий виконавець" #~ msgid "Play music" #~ msgstr "Відтворення музики" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Close" #~ msgstr "Закрити" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "" #~ "Перетягування зі скиданням з «Файлів GNOME», показ музичних даних у " #~ "«Файлах»." #~ msgid "Prefer dark theme" #~ msgstr "Перевага темної теми" #~ msgid "_Add to Playlist…" #~ msgstr "_Додати до списку відтворення…" #~ msgid "G4Music" #~ msgstr "G4Music" ================================================ FILE: po/zh_CN.po ================================================ # Chinese translations for g4music package. # Copyright (C) 2022 THE g4music'S COPYRIGHT HOLDER # This file is distributed under the same license as the g4music package. # lumingzh , 2024. # Nanling , 2022-2024. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-11 09:34+0800\n" "PO-Revision-Date: 2024-10-11 09:35+0800\n" "Last-Translator: Nanling \n" "Language-Team: Chinese - China \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-Generator: Gtranslator 46.1\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "无暇" #: data/app.desktop.in:4 msgid "Music Player" msgstr "音乐播放器" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "优雅地播放您的音乐" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;音乐;声音;播放器;媒体;音频;播放列表;" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "无暇(又名:G4Music)是一款用 GTK4 编写的轻量级音乐播放器,专注于大型音乐收" "藏。" #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "特点" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "支持大多数音乐文件类型、Samba 和任何其他远程协议(取决于 GIO 和 GStreamer)。" #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "在数秒内快速加载和解析数千个音乐文件,监控本地文件变化。" #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "对于带有专辑封面(嵌入式和外置式)的大型音乐收藏,内存使用率低,无需存储缩略" "图缓存。" #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "按专辑/艺术家/标题分组和排序、随机播放列表、全文搜索。" #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "针对不同屏幕(台式机、平板电脑、手机)的流畅自适应用户界面。" #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "高斯模糊封面作为背景,跟随 GNOME 亮色/暗色模式。" #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "支持创建和编辑播放列表,拖拽封面更改排序或添加到另一个播放列表。" #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "支持通过其它应用拖放文件。" #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "支持音频峰值可视化。" #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "支持无缝播放。" #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "支持通过回放增益实现音量平衡。" #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "支持指定音频输出。" #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "支持 MPRIS 控制。" #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "主窗口" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "专辑视图" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "正在播放视图" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "播放列表视图" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "常规" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "显示快捷键" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "首选项" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "退出" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "显示标签" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "播放" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "播放/暂停" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "播放下一首" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "播放上一首" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "播放列表" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "搜索" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "切换排序" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "重载曲库" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "保存列表" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "返回" #: src/gtk/preferences.ui:10 msgid "General" msgstr "常规" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "背景模糊模式" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "紧凑播放列表" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "艺术家/专辑使用网格视图" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "单击激活项目" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "关闭窗口后继续播放" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "曲库" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "从文件夹加载音乐" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "监控本地文件变化" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "加载非本地文件的缩略图" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "可能会导致速度减慢和过多的网络使用" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "播放" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "显示音频峰值" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "显示的字符" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "旋转专辑封面" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "启用无缝续播" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "自动音量平衡" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "首选音频输出" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "主菜单" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "搜索" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "排序" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "配色方案(_C)" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "系统(_S)" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "亮色(_L)" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "暗色(_D)" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "重载曲库(_R)" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "多重选择(_M)" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "保存列表(_S)" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "首选项(_P)" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "键盘快捷键(_K)" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "关于(_A)" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "专辑" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "艺术家" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "艺术家/专辑" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "标题" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "最近" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "乱序" #: src/action-handles.vala:114 msgid "Image Files" msgstr "图像文件" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "导出封面成功" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "导出封面失败" #: src/application.vala:350 msgid "Save playlist failed" msgstr "保存播放列表失败" #: src/application.vala:447 msgid "Playlist Files" msgstr "播放列表文件" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "保存播放列表成功" #: src/application.vala:597 msgid "Keep playing" msgstr "持续播放" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "一个快速、流畅、轻量的,基于 GTK4 的音乐播放器。" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Nanling , 2022-2024.\n" "lumingzh , 2024." #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "否" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "是" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "播放列表已修改,是否保存?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "插播(_N)" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "添加到队列(_Q)" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "添加到播放列表(_P)…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "移除(_R)" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "移至回收站(_M)" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "全选" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "插播" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "添加到队列" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "添加到播放列表" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "移除" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "播放" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "随机播放(_R)" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "显示列表文件(_F)" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "搜索标题" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "搜索专辑" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "搜索艺术家" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "显示封面文件(_F)" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "导出封面(_E)" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "显示标签(_T)…" #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "显示音乐文件(_F)" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "单曲循环" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "播放上一首" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "播放/暂停" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "播放下一首" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "拖放音乐文件到这里,\n" "或者修改曲库的位置:" #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "新建播放列表" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "在 %s 中未找到播放列表" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "从不" #: src/ui/preferences.vala:44 msgid "Always" msgstr "总是" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "仅封面" #: src/ui/preferences.vala:66 msgid "Track" msgstr "曲目" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "正在播放" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "艺术家" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "专辑" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "播放列表" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "复制" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "音量 %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "显示" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "未知专辑" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "未知艺术家" #~ msgid "Play music" #~ msgstr "播放音乐" #~ msgid "Close" #~ msgstr "关闭" #~ msgid "Nanling" #~ msgstr "Nanling" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "从 GNOME 文件管理器拖放音乐文件,显示文件管理器中的音乐文件。" #~ msgid "Prefer dark theme" #~ msgstr "首选深色主题" #, fuzzy, c-format #~ msgid "No playlist found in: %s" #~ msgstr "未找到播放列表" #~ msgid "_Add to Playlist…" #~ msgstr "添加到播放列表(_A)…" ================================================ FILE: po/zh_TW.po ================================================ # Traditional Chinese translations for g4music package # g 套件的繁體中文翻譯. # Copyright (C) 2022 THE g4music'S COPYRIGHT HOLDER # This file is distributed under the same license as the g package. # Julian , 2022. # Nanling , 2023-2024. # msgid "" msgstr "" "Project-Id-Version: g4music\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-11 09:34+0800\n" "PO-Revision-Date: 2024-10-11 09:36+0800\n" "Last-Translator: Nanling \n" "Language-Team: Chinese - China \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-Generator: Gtranslator 46.1\n" #: data/app.desktop.in:3 data/app.metainfo.xml.in:4 src/application.vala:277 #: src/main.vala:24 msgid "Gapless" msgstr "無暇" #: data/app.desktop.in:4 msgid "Music Player" msgstr "音樂播放器" #: data/app.desktop.in:5 data/app.metainfo.xml.in:7 msgid "Play your music elegantly" msgstr "優雅地播放您的音樂" #: data/app.desktop.in:12 msgid "music;sound;player;media;audio;playlist;" msgstr "" "music;sound;player;media;audio;playlist;音樂;聲音;播放器;媒體;音訊;播放列表" #: data/app.metainfo.xml.in:24 msgid "" "Gapless (AKA: G4Music) is a light weight music player written in GTK4, " "focuses on large music collection." msgstr "" "無暇 (又名: G4Music) 是以 GTK4 寫成的輕量級音樂播放器,專注於大型音樂收藏。" #: data/app.metainfo.xml.in:25 msgid "Features" msgstr "特點" #: data/app.metainfo.xml.in:27 msgid "" "Supports most music file types, Samba and any other remote protocols " "(depends on GIO and GStreamer)." msgstr "" "支援大多數音樂檔案類型、Samba 及任何其他遠端通訊協定 (取決於 GIO 及 " "GStreamer)。" #: data/app.metainfo.xml.in:28 msgid "" "Fast loading and parsing thousands of music files in very few seconds, " "monitor local changes." msgstr "在幾秒鐘內快速載入和解析數以千計的音樂檔案,監控本機變更。" #: data/app.metainfo.xml.in:29 msgid "" "Low memory usage for large music collection with album covers (embedded and " "external), no thumbnail caches to store." msgstr "" "對於包含專輯封面 (內嵌與外接) 的大型音樂收藏,記憶體使用量低,無須儲存縮圖快" "取。" #: data/app.metainfo.xml.in:30 msgid "" "Group and sorts by album/artist/title, shuffle list, full-text searching." msgstr "依專輯/歌手/標題分類與排序、隨機播放清單、全文檢索。" #: data/app.metainfo.xml.in:31 msgid "" "Fluent adaptive user interface for different screen (Desktop, Tablet, " "Mobile)." msgstr "" "適用於不同螢幕 (桌上型電腦、平板電腦、行動裝置) 的流暢自適應使用者介面。" #: data/app.metainfo.xml.in:32 msgid "Gaussian blurred cover as background, follows GNOME light/dark mode." msgstr "高斯模糊封面作為背景,遵循 GNOME 明暗模式。" #: data/app.metainfo.xml.in:33 msgid "" "Supports creating and editing playlists, drag cover to change order or add " "to another playlist." msgstr "支援建立與編輯播放清單,拖曳封面可變更順序或加入另一個播放清單。" #: data/app.metainfo.xml.in:34 msgid "Supports drag and drop with other apps." msgstr "支援與其他應用拖放功能。" #: data/app.metainfo.xml.in:35 msgid "Supports audio peaks visualizer." msgstr "支援音訊峰值顯示器。" #: data/app.metainfo.xml.in:36 msgid "Supports gapless playback." msgstr "支援無間隙播放。" #: data/app.metainfo.xml.in:37 msgid "Supports normalizing volume with ReplayGain." msgstr "支援使用 ReplayGain 將音量正常化。" #: data/app.metainfo.xml.in:38 msgid "Supports specified audio sink." msgstr "支援指定的音訊輸出。" #: data/app.metainfo.xml.in:39 msgid "Supports MPRIS control." msgstr "支援 MPRIS 控制。" #: data/app.metainfo.xml.in:50 msgid "Main window" msgstr "主視窗" #: data/app.metainfo.xml.in:54 msgid "Albums view" msgstr "專輯檢視" #: data/app.metainfo.xml.in:58 msgid "Playing view" msgstr "正在播放檢視" #: data/app.metainfo.xml.in:62 msgid "Playlist view" msgstr "播放清單檢視" #: src/gtk/help-overlay.ui:11 msgctxt "shortcut window" msgid "General" msgstr "一般" #: src/gtk/help-overlay.ui:14 msgctxt "shortcut window" msgid "Show Shortcuts" msgstr "顯示快捷鍵" #: src/gtk/help-overlay.ui:20 msgctxt "shortcut window" msgid "Preferences" msgstr "偏好設定" #: src/gtk/help-overlay.ui:26 msgctxt "shortcut window" msgid "Quit" msgstr "退出" #: src/gtk/help-overlay.ui:32 msgctxt "shortcut window" msgid "Show Tags" msgstr "顯示標籤" #: src/gtk/help-overlay.ui:40 msgctxt "shortcut window" msgid "Playback" msgstr "播放" #: src/gtk/help-overlay.ui:43 msgctxt "shortcut window" msgid "Play/Pause" msgstr "播放/暫停" #: src/gtk/help-overlay.ui:49 msgctxt "shortcut window" msgid "Play Next" msgstr "播放下一首" #: src/gtk/help-overlay.ui:55 msgctxt "shortcut window" msgid "Play Previous" msgstr "播放上一首" #: src/gtk/help-overlay.ui:63 msgctxt "shortcut window" msgid "Playlist" msgstr "播放清單" #: src/gtk/help-overlay.ui:66 msgctxt "shortcut window" msgid "Search" msgstr "搜尋" #: src/gtk/help-overlay.ui:72 msgctxt "shortcut window" msgid "Toggle Sort" msgstr "切換排序" #: src/gtk/help-overlay.ui:78 msgctxt "shortcut window" msgid "Reload Library" msgstr "更新音樂庫" #: src/gtk/help-overlay.ui:84 msgctxt "shortcut window" msgid "Save List" msgstr "儲存清單" #: src/gtk/play-panel.ui:16 src/ui/music-list.vala:629 #: src/ui/store-panel.vala:421 msgid "Back" msgstr "返回" #: src/gtk/preferences.ui:10 msgid "General" msgstr "一般" #: src/gtk/preferences.ui:14 msgid "Background blur mode" msgstr "背景模糊模式" #: src/gtk/preferences.ui:20 msgid "Compact playlist view" msgstr "緊湊播放列表" #: src/gtk/preferences.ui:32 msgid "Grid view for artists/albums" msgstr "藝術家/專輯使用網格檢視" #: src/gtk/preferences.ui:44 msgid "Single click to activate item" msgstr "單擊啟動項目" #: src/gtk/preferences.ui:56 src/application.vala:425 msgid "Keep playing after window closed" msgstr "關閉視窗後繼續播放" #: src/gtk/preferences.ui:69 msgid "Library" msgstr "音樂庫" #: src/gtk/preferences.ui:73 msgid "Load music from folder" msgstr "從資料夾載入音樂" #: src/gtk/preferences.ui:85 msgid "Monitor local file changes" msgstr "監控本地檔案變化" #: src/gtk/preferences.ui:97 msgid "Load thumbnails for non-local files" msgstr "載入非本機檔案的縮圖" #: src/gtk/preferences.ui:98 msgid "May cause slowdowns and excess network usage" msgstr "可能會導致速度減慢與增加網路使用" #: src/gtk/preferences.ui:111 msgid "Playback" msgstr "播放" #: src/gtk/preferences.ui:115 msgid "Show audio peak level" msgstr "顯示音訊峰值" #: src/gtk/preferences.ui:119 msgid "Display characters" msgstr "顯示的字元" #: src/gtk/preferences.ui:134 msgid "Rotate album cover" msgstr "旋轉專輯封面" #: src/gtk/preferences.ui:146 msgid "Enable gapless playback" msgstr "啟用無縫播放" #: src/gtk/preferences.ui:158 msgid "Normalize volume with ReplayGain" msgstr "使用 ReplayGain 正規化音量" #: src/gtk/preferences.ui:164 msgid "Prefer audio sink of GStreamer" msgstr "首選音訊輸出" #: src/gtk/store-panel.ui:22 msgid "Main Menu" msgstr "主選單" #: src/gtk/store-panel.ui:29 src/ui/playlist-dialog.vala:36 msgid "Search" msgstr "搜尋" #: src/gtk/store-panel.ui:39 msgid "Sort By" msgstr "排序" #: src/gtk/store-panel.ui:70 msgid "_Color Scheme" msgstr "配色方案(_C)" #: src/gtk/store-panel.ui:72 msgid "_System" msgstr "系統(_S)" #: src/gtk/store-panel.ui:77 msgid "_Light" msgstr "亮色(_L)" #: src/gtk/store-panel.ui:82 msgid "_Dark" msgstr "暗色(_D)" #: src/gtk/store-panel.ui:89 msgid "_Reload Library" msgstr "更新音樂庫(_R)" #: src/gtk/store-panel.ui:93 msgid "_Multiple Select" msgstr "多重選擇(_M)" #: src/gtk/store-panel.ui:97 msgid "_Save List" msgstr "儲存清單(_S)" #: src/gtk/store-panel.ui:103 msgid "_Preferences" msgstr "偏好設定(_P)" #: src/gtk/store-panel.ui:107 msgid "_Keyboard Shortcuts" msgstr "快捷鍵(_K)" #: src/gtk/store-panel.ui:111 msgid "_About" msgstr "關於(_A)" #: src/gtk/store-panel.ui:120 src/ui/preferences.vala:66 msgid "Album" msgstr "專輯" #: src/gtk/store-panel.ui:125 msgid "Artist" msgstr "藝術家" #: src/gtk/store-panel.ui:130 msgid "Artist/Album" msgstr "藝術家/專輯" #: src/gtk/store-panel.ui:135 msgid "Title" msgstr "歌名" #: src/gtk/store-panel.ui:140 msgid "Recent" msgstr "最近" #: src/gtk/store-panel.ui:145 msgid "Shuffle" msgstr "亂序播放" #: src/action-handles.vala:114 msgid "Image Files" msgstr "圖像檔案" #: src/action-handles.vala:121 msgid "Export cover successfully" msgstr "輸出封面成功" #: src/action-handles.vala:121 msgid "Export cover failed" msgstr "輸出封面失败" #: src/application.vala:350 msgid "Save playlist failed" msgstr "儲存播放清單失敗" #: src/application.vala:447 msgid "Playlist Files" msgstr "播放清單檔案" #: src/application.vala:461 msgid "Save playlist successfully" msgstr "儲存播放清單成功" #: src/application.vala:597 msgid "Keep playing" msgstr "持續播放" #: src/ui/dialogs.vala:51 msgid "A fast, fluent, light weight music player written in GTK4." msgstr "一個快速、流暢、輕量,基於 GTK4 的音樂播放軟體。" #. Translators: Replace "translator-credits" with your names, one name per line #: src/ui/dialogs.vala:53 msgid "translator-credits" msgstr "" "Julian\n" "Nanling" #: src/ui/dialogs.vala:99 src/ui/dialogs.vala:111 msgid "No" msgstr "否" #: src/ui/dialogs.vala:100 src/ui/dialogs.vala:111 msgid "Yes" msgstr "是" #: src/ui/music-list.vala:272 msgid "Playlist is modified, save it?" msgstr "播放清單已修改,要儲存嗎?" #. Translators: Play this music at next position of current playing music #: src/ui/music-list.vala:402 src/ui/music-list.vala:418 #: src/ui/music-widgets.vala:277 src/ui/music-widgets.vala:299 msgid "Play at _Next" msgstr "插播(_N)" #: src/ui/music-list.vala:404 src/ui/music-list.vala:420 #: src/ui/music-widgets.vala:278 src/ui/music-widgets.vala:300 msgid "Add to _Queue" msgstr "加入列隊(_Q)" #: src/ui/music-list.vala:405 src/ui/music-widgets.vala:279 #: src/ui/music-widgets.vala:301 src/ui/music-widgets.vala:320 msgid "Add to _Playlist…" msgstr "加入播放清單(_P)…" #: src/ui/music-list.vala:406 src/ui/music-list.vala:424 msgid "_Remove" msgstr "移除(_R)" #: src/ui/music-list.vala:425 src/ui/music-widgets.vala:286 msgid "_Move to Trash" msgstr "移至垃圾桶(_M)" #: src/ui/music-list.vala:633 msgid "Select All" msgstr "全選" #: src/ui/music-list.vala:650 msgid "Play at Next" msgstr "插播" #: src/ui/music-list.vala:657 msgid "Add to Queue" msgstr "加入列隊" #: src/ui/music-list.vala:664 src/ui/playlist-dialog.vala:19 msgid "Add to Playlist" msgstr "加入播放清單" #: src/ui/music-list.vala:671 msgid "Remove" msgstr "移除" #: src/ui/music-widgets.vala:274 src/ui/music-widgets.vala:296 #: src/ui/store-panel.vala:429 msgid "Play" msgstr "播放" #: src/ui/music-widgets.vala:275 src/ui/music-widgets.vala:297 msgid "_Random Play" msgstr "隨機播放(_R)" #: src/ui/music-widgets.vala:285 msgid "Show List _File" msgstr "顯示清單檔案(_F)" #: src/ui/music-widgets.vala:308 src/ui/play-panel.vala:60 msgid "Search Title" msgstr "搜尋歌名" #: src/ui/music-widgets.vala:309 src/ui/play-panel.vala:58 msgid "Search Album" msgstr "搜尋專輯" #: src/ui/music-widgets.vala:310 src/ui/play-panel.vala:59 msgid "Search Artist" msgstr "搜尋藝術家" #: src/ui/music-widgets.vala:314 msgid "Show Cover _File" msgstr "顯示封面檔案(_F)" #: src/ui/music-widgets.vala:316 msgid "_Export Cover" msgstr "輸出封面(_E)" #: src/ui/music-widgets.vala:317 msgid "Show _Tags…" msgstr "顯示標籤(_T)..." #: src/ui/music-widgets.vala:318 msgid "Show Music _File" msgstr "顯示音樂檔案(_F)" #. Translators: single loop the current music #: src/ui/play-bar.vala:72 msgid "Single Loop" msgstr "單曲迴圈" #: src/ui/play-bar.vala:82 msgid "Play Previous" msgstr "播放上一首" #. media-playback-pause-symbolic #: src/ui/play-bar.vala:88 msgid "Play/Pause" msgstr "播放/暫停" #: src/ui/play-bar.vala:95 msgid "Play Next" msgstr "播放下一首" #: src/ui/play-panel.vala:255 msgid "" "Drag and drop music files here,\n" "or change music location: " msgstr "" "將音樂拖曳至這裡,\n" "或者修改音樂庫位置:" #: src/ui/playlist-dialog.vala:24 msgid "New Playlist" msgstr "新建播放清單" #: src/ui/playlist-dialog.vala:98 src/ui/store-panel.vala:692 #, c-format msgid "No playlist found in %s" msgstr "在 %s 中找不到播放清單" #: src/ui/preferences.vala:44 src/ui/preferences.vala:66 msgid "Never" msgstr "從不" #: src/ui/preferences.vala:44 msgid "Always" msgstr "總是" #: src/ui/preferences.vala:44 msgid "Art Only" msgstr "僅封面" #: src/ui/preferences.vala:66 msgid "Track" msgstr "曲目" #: src/ui/store-panel.vala:84 msgid "Playing" msgstr "正在播放" #: src/ui/store-panel.vala:89 msgid "Artists" msgstr "藝術家" #: src/ui/store-panel.vala:94 msgid "Albums" msgstr "專輯" #: src/ui/store-panel.vala:99 msgid "Playlists" msgstr "播放清單" #: src/ui/taglist-dialog.vala:125 msgid "Copy" msgstr "複製" #. Translators: Current volume percent: 0~100% #: src/ui/volume-button.vala:33 #, c-format msgid "Volume %d%%" msgstr "音量 %d%%" #: src/ui/window.vala:112 msgid "Show" msgstr "顯示" #: src/utils/music.vala:2 msgid "Unknown Album" msgstr "未知專輯" #: src/utils/music.vala:3 msgid "Unknown Artist" msgstr "未知藝術家" #~ msgid "Play music" #~ msgstr "播放音樂" #~ msgid "Close" #~ msgstr "关闭" #~ msgid "Drag-drop from GNOME Files, showing music in Files." #~ msgstr "從 GNOME 檔案拖放、顯示檔案中的音樂。" #~ msgid "Prefer dark theme" #~ msgstr "首選暗色主題" #, fuzzy, c-format #~ msgid "No playlist found in: %s" #~ msgstr "未找到播放清單" ================================================ FILE: src/action-handles.vala ================================================ namespace G4 { public const string ACTION_APP = "app."; public const string ACTION_ABOUT = "about"; public const string ACTION_PREFS = "preferences"; public const string ACTION_ADD_TO_PLAYLIST = "add-to-playlist"; public const string ACTION_ADD_TO_QUEUE = "add-to-queue"; public const string ACTION_EXPORT_COVER = "export-cover"; public const string ACTION_PLAY = "play"; public const string ACTION_PLAY_AT_NEXT = "play-at-next"; public const string ACTION_PLAY_PAUSE = "play-pause"; public const string ACTION_PREV = "prev"; public const string ACTION_NEXT = "next"; public const string ACTION_RANDOM_PLAY = "random-play"; public const string ACTION_RELOAD = "reload"; public const string ACTION_REMOVE = "remove"; public const string ACTION_SAVE_LIST = "save-list"; public const string ACTION_SCHEME = "scheme"; public const string ACTION_SHOW_FILE = "show-file"; public const string ACTION_SHOW_TAGS = "show-tags"; public const string ACTION_SHOW_TAGS_CURRENT = "show-cur-tags"; public const string ACTION_SORT = "sort"; public const string ACTION_TOGGLE_SORT = "toggle-sort"; public const string ACTION_TRASH_FILE = "trash-file"; public const string ACTION_QUIT = "quit"; public const string ACTION_WIN = "win."; public const string ACTION_BUTTON = "button"; public const string ACTION_SEARCH = "search"; public const string ACTION_SELECT = "select"; public const string ACTION_TOGGLE_SEARCH = "toggle-search"; struct ActionShortKey { public unowned string name; public unowned string key; } public class ActionHandles : Object { private Application _app; private Portal _portal = new Portal (); public ActionHandles (Application app) { _app = app; ActionEntry[] action_entries = { { ACTION_ABOUT, () => show_about_dialog (_app) }, { ACTION_ADD_TO_PLAYLIST, add_to_playlist, "s" }, { ACTION_ADD_TO_QUEUE, play_or_queue, "s" }, { ACTION_EXPORT_COVER, export_cover, "s" }, { ACTION_NEXT, () => _app.play_next () }, { ACTION_PLAY, play_or_queue, "s" }, { ACTION_PLAY_AT_NEXT, play_at_next, "s" }, { ACTION_PLAY_PAUSE, () => _app.play_pause () }, { ACTION_PREV, () => _app.play_previous () }, { ACTION_PREFS, show_preferences }, { ACTION_RANDOM_PLAY, play_or_queue, "s" }, { ACTION_RELOAD, () => _app.reload_library () }, { ACTION_SCHEME, scheme, "s", "'0'" }, { ACTION_SHOW_FILE, show_file, "s" }, { ACTION_SHOW_TAGS, show_tags, "s" }, { ACTION_SHOW_TAGS_CURRENT, show_tags }, { ACTION_SORT, sort_by, "s", "'2'" }, { ACTION_TOGGLE_SORT, toggle_sort }, { ACTION_TRASH_FILE, trash_file, "s" }, { ACTION_QUIT, () => _app.quit () } }; app.add_action_entries (action_entries, this); ActionShortKey[] app_keys = { { ACTION_PREFS, "comma" }, { ACTION_PLAY_PAUSE, "p" }, { ACTION_PREV, "Left" }, { ACTION_NEXT, "Right" }, { ACTION_RELOAD, "r" }, { ACTION_SHOW_TAGS_CURRENT, "t" }, { ACTION_TOGGLE_SORT, "m" }, { ACTION_QUIT, "q" } }; foreach (var item in app_keys) { app.set_accels_for_action (ACTION_APP + item.name, {item.key}); } ActionShortKey[] win_keys = { { ACTION_SAVE_LIST, "s" }, { ACTION_TOGGLE_SEARCH, "f" }, }; foreach (var item in win_keys) { app.set_accels_for_action (ACTION_WIN + item.name, {item.key}); } } public Portal portal { get { return _portal; } } private async void _export_cover_async (Music music) { var cover = _app.current_cover; if (cover == null || music != _app.current_music) { var file = File.new_for_uri (music.uri); cover = yield run_async (() => { var tags = parse_gst_tags (file); return tags != null ? parse_image_from_tag_list ((!)tags) : null; }); } if (cover != null) { var sample = (!)cover; var itype = sample.get_caps ()?.get_structure (0)?.get_name (); var pos = itype?.index_of_char ('/') ?? -1; var ext = itype?.substring (pos + 1) ?? ""; var name = music.get_artist_and_title ().replace ("/", "&") + "." + ext; var filter = new Gtk.FileFilter (); filter.name = _("Image Files"); filter.add_mime_type ("image/*"); var initial = File.new_build_filename (name); var file = yield show_save_file_dialog (_app.active_window, initial, {filter}); if (file != null) { var saved = yield save_sample_to_file_async ((!)file, sample); (_app.active_window as Window)?.show_toast ( saved ? _("Export cover successfully") : _("Export cover failed"), saved ? file?.get_uri () : (string?) null); } } } private void add_to_playlist (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); var playlist = parse_playlist_from_music_uri (uri); if (playlist != null) { _app.show_add_playlist_dialog.begin ((!)playlist, (obj, res) => _app.show_add_playlist_dialog.end (res)); } } private void export_cover (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); var music = uri != null ? _app.loader.find_cache ((!)uri) : null; if (music != null) _export_cover_async.begin ((!)music, (obj, res) => _export_cover_async.end (res)); } private Playlist? parse_playlist_from_music_uri (string? uri) { Music? node = null; if (uri != null) { string? ar = null, al = null, pl = null; parse_library_uri ((!)uri, out ar, out al, out pl); var loader = _app.loader; var library = loader.library; if (ar != null) { var artist = library.get_artist ((!)ar); if (artist != null && al != null && ((!)al).length > 0) node = ((!)artist)[(!)al]; else node = artist; } else if (al != null) { node = library.get_album ((!)al); } else if (pl != null) { node = library.get_playlist ((!)pl); } else { node = loader.find_cache ((!)uri); } } return node != null ? to_playlist ({(!)node}) : (Playlist?) null; } private void play_or_queue (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); if (action.name.has_suffix (ACTION_ADD_TO_QUEUE)) { var playlist = parse_playlist_from_music_uri (uri); if (playlist != null) _app.insert_to_queue ((!)playlist); } else if (uri != null) { Window.get_default ()?.open_page ((!)uri, true, action.name.has_suffix (ACTION_RANDOM_PLAY)); } } private void play_at_next (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); var playlist = parse_playlist_from_music_uri (uri); if (playlist != null) _app.insert_after_current ((!)playlist); } private void scheme (SimpleAction action, Variant? state) { uint scheme = 0; if (uint.try_parse (state?.get_string () ?? "", out scheme)) _app.settings.set_uint ("color-scheme", scheme); } private void show_file (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); if (uri != null) { if (((!)uri).has_prefix (LIBRARY_SCHEME)) { Window.get_default ()?.open_page ((!)uri, false); } else { _portal.open_directory_async.begin ((!)uri, (obj, res) => { try { _portal.open_directory_async.end (res); } catch (Error e) { Window.get_default ()?.show_toast (e.message); } }); } } } private void show_tags (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string () ?? _app.current_music?.uri; if (uri != null) { var tags = strcmp (_app.current_music?.uri, uri) == 0 ? _app.player.tag_list : (Gst.TagList?) null; var dialog = new TagListDialog ((!)uri, tags); dialog.present (Window.get_default ()); } } private PreferencesWindow? _pref_window = null; private void show_preferences () { var win = _pref_window ?? new PreferencesWindow (_app); _pref_window = win; win.destroy_with_parent = true; win.modal = false; win.transient_for = _app.active_window; win.close_request.connect (() => { _pref_window = null; return false; }); win.present (); } private void sort_by (SimpleAction action, Variant? state) { unowned var value = state?.get_string () ?? ""; int mode = 2; if (int.try_parse (value, out mode)) _app.sort_mode = mode; } private void toggle_sort () { if (_app.sort_mode >= SortMode.MAX) _app.sort_mode = SortMode.ALBUM; else _app.sort_mode = _app.sort_mode + 1; } private void trash_file (SimpleAction action, Variant? parameter) { var uri = parameter?.get_string (); if (uri != null) { _portal.trash_file_async.begin ((!)uri, (obj, res) => { try { if (_portal.trash_file_async.end (res)) { _app.loader.on_file_removed.begin (File.new_for_uri ((!)uri), (obj, res) => _app.loader.on_file_removed.end (res)); } } catch (Error e) { Window.get_default ()?.show_toast (e.message); } }); } } } } ================================================ FILE: src/application.vala ================================================ namespace G4 { public class Application : Adw.Application { private ActionHandles? _actions = null; private int _current_index = -1; private uint _current_list_size = 0; private Music? _current_music = null; private string _current_uri = ""; private Gst.Sample? _current_cover = null; private bool _list_modified = false; private bool _loading = false; private string _music_folder = ""; private uint _mpris_id = 0; private MusicLoader _loader = new MusicLoader (); private Gtk.FilterListModel _current_list = new Gtk.FilterListModel (null, null); private ListStore _music_queue = new ListStore (typeof (Music)); private StringBuilder _next_uri = new StringBuilder (); private GstPlayer _player = new GstPlayer (); private Settings _settings; private uint _sort_mode = SortMode.TITLE; private bool _store_external_changed = false; private Thumbnailer _thumbnailer = new Thumbnailer (); public signal void index_changed (int index, uint size); public signal void music_changed (Music? music); public signal void music_cover_parsed (Music music, Gdk.Pixbuf? cover, string? cover_uri); public signal void music_library_changed (bool external); public signal void playlist_added (Playlist playlist); public signal void thumbnail_changed (Music music, Gdk.Paintable paintable); public Application () { Object (application_id: Config.APP_ID, flags: ApplicationFlags.HANDLES_OPEN); } public override void startup () { base.startup (); // Must load tag cache after the app register (GLib init), to make sort works _loader.load_tag_cache (); _actions = new ActionHandles (this); _current_list.model = _music_queue; _current_list.items_changed.connect (on_music_list_changed); _music_queue.items_changed.connect (on_music_library_changed); _loader.loading_changed.connect ((loading) => _loading = loading); _loader.music_found.connect (on_music_found); _loader.music_lost.connect (on_music_lost); _thumbnailer.cover_finder = _loader.cover_cache; _thumbnailer.tag_updated.connect (_loader.add_to_cache); _player.end_of_stream.connect (on_player_end); _player.error.connect (on_player_error); _player.next_uri_request.connect (on_player_next_uri_request); _player.next_uri_start.connect (on_player_next_uri_start); _player.state_changed.connect (on_player_state_changed); _player.tag_parsed.connect (on_player_tag_parsed); _mpris_id = Bus.own_name (BusType.SESSION, "org.mpris.MediaPlayer2." + application_id, BusNameOwnerFlags.NONE, on_bus_acquired, null, null ); if (_mpris_id == 0) warning ("Initialize MPRIS session failed\n"); var settings = _settings = new Settings (application_id); settings.bind ("color-scheme", this, "color-scheme", SettingsBindFlags.DEFAULT); settings.bind ("music-dir", this, "music-folder", SettingsBindFlags.DEFAULT); settings.bind ("sort-mode", this, "sort-mode", SettingsBindFlags.DEFAULT); settings.bind ("monitor-changes", _loader, "monitor-changes", SettingsBindFlags.DEFAULT); settings.bind ("remote-thumbnail", _thumbnailer, "remote-thumbnail", SettingsBindFlags.DEFAULT); settings.bind ("gapless-playback", _player, "gapless", SettingsBindFlags.DEFAULT); settings.bind ("replay-gain", _player, "replay-gain", SettingsBindFlags.DEFAULT); settings.bind ("audio-sink", _player, "audio-sink", SettingsBindFlags.DEFAULT); settings.bind ("volume", _player, "volume", SettingsBindFlags.DEFAULT); } public override void activate () { base.activate (); var window = Window.get_default (); if (window != null) { ((!)window).present (); } else { open ({}, ""); } } public override void open (File[] files, string hint) { var window = Window.get_default (); var initial = window == null; (window ?? new Window (this))?.present (); if (initial && _current_music == null) { var folders = files; folders.resize (folders.length + 1); folders[folders.length - 1] = File.new_for_uri (music_folder); var recent_uri = _current_music?.uri ?? _settings.get_string ("recent-music"); foreach (var file in folders) { if (recent_uri.has_prefix (file.get_uri ())) { // 1.Load recent played uri if in folders _current_music = new Music (recent_uri, "", 0); _player.uri = _current_uri = recent_uri; _player.state = Gst.State.PAUSED; break; } } } var saved_modified = _list_modified; var plist_file = get_playing_list_file (); var load_plist = initial && files.length == 0; var files_ref = load_plist ? new File[] { plist_file } : files; // 2.Load last playing queue if no other files to load open_files_async.begin (files_ref, -1, files.length > 0, (obj, res) => { var ret = open_files_async.end (res); if (ret) { _list_modified = saved_modified; if (load_plist) _loader.library.remove_playlist (plist_file.get_uri ()); } if (initial) { // 3. Load music folder to build the library load_music_folder_async.begin ((obj, res) => load_music_folder_async.end (res)); } }); } public override void shutdown () { _actions = null; _loader.save_tag_cache (); delete_cover_tmp_file_async.begin ((obj, res) => delete_cover_tmp_file_async.end (res)); if (_mpris_id != 0) { Bus.unown_name (_mpris_id); _mpris_id = 0; } base.shutdown (); } public uint color_scheme { get { return (uint) style_manager.color_scheme; } set { var action = lookup_action (ACTION_SCHEME); (action as SimpleAction)?.set_state (value.to_string ()); style_manager.color_scheme = (Adw.ColorScheme) value; } } public unowned Gst.Sample? current_cover { get { return _current_cover; } } public int current_item { get { return _current_index; } set { if (value >= (int) _current_list.get_n_items ()) { value = Window.get_default ()?.open_next_playable_page () ?? value; } current_music = _current_list.get_item (value) as Music; _current_index = value; _current_list_size = _current_list.get_n_items (); index_changed (value, _current_list_size); update_next_item (); } } public Gtk.FilterListModel current_list { get { return _current_list; } set { if (_current_list != value) { _current_list.items_changed.disconnect (on_music_list_changed); _current_list = value; _current_list.items_changed.connect (on_music_list_changed); update_current_item (); } } } public Music? current_music { get { return _current_music; } set { var playing = _current_music != null || _player.state == Gst.State.PLAYING; if (_current_music != value || value == null) { _current_music = value; music_changed (value); } var uri = value?.uri ?? ""; if (_current_uri != uri) { _current_cover = null; _player.state = Gst.State.READY; _player.uri = _current_uri = uri; if (uri.length > 0) _player.state = playing ? Gst.State.PLAYING : Gst.State.PAUSED; } _settings.set_string ("recent-music", uri); } } private Gtk.IconPaintable? _icon = null; public Gtk.IconPaintable? icon { get { if (_icon == null) { var theme = Gtk.IconTheme.get_for_display (active_window.display); _icon = theme.lookup_icon (application_id, null, _cover_size, active_window.scale_factor, Gtk.TextDirection.NONE, Gtk.IconLookupFlags.FORCE_REGULAR); } return _icon; } } public bool list_modified { get { return _list_modified; } set { _list_modified = value; } } public MusicLoader loader { get { return _loader; } } public bool loading { get { return _loading; } } public string music_folder { get { if (_music_folder.length == 0) { var path = ((string?) Environment.get_user_special_dir (UserDirectory.MUSIC)) ?? "Music"; _music_folder = File.new_build_filename (path).get_uri (); } return _music_folder; } set { if (_music_folder != value) { _music_folder = value; if (Window.get_default () != null) reload_library (); } } } public ListStore music_queue { get { return _music_queue; } } public string name { get { return _("Gapless"); } } public GstPlayer player { get { return _player; } } public Settings settings { get { return _settings; } } public bool single_loop { get; set; } public uint sort_mode { get { return _sort_mode; } set { var action = lookup_action (ACTION_SORT); var state = new Variant.string (value.to_string ()); (action as SimpleAction)?.set_state (state); if (_sort_mode != value) { _sort_mode = value; sort_music_store ((ListStore) _music_queue, value); } } } public Thumbnailer thumbnailer { get { return _thumbnailer; } } public async bool add_playlist_to_file_async (Playlist playlist, bool append) { var file = File.new_for_uri (playlist.list_uri); var uris = new GenericArray (1024); var saved = yield run_async (() => { string? title = null; var map = new GenericSet (str_hash, str_equal); if (append) { title = load_playlist_file (file, uris); uris.foreach ((uri) => map.add (uri)); } foreach (var music in playlist.items) { var uri = music.uri; if (!map.contains (uri)) uris.add (uri); } var ret = save_playlist_file (file, uris, title ?? playlist.title); if (ret) { // Replace items if loaded from existing file playlist.clear (); foreach (var uri in uris) { var music = _loader.find_cache (uri); if (music != null) playlist.add_music ((!)music); } playlist.set_cover_uri (); if (title != null) playlist.set_title ((!)title); } return ret; }); if (saved) playlist_added (_loader.library.add_playlist (playlist)); else Window.get_default ()?.show_toast (_("Save playlist failed")); return saved; } public int insert_after_current (Playlist playlist) { uint position = _current_index; if (_current_music != null && _music_queue.find ((!)_current_music, out position)) { position++; } var changed = merge_items_to_store (_music_queue, playlist.items, ref position); list_modified |= changed; if (changed) { update_current_item (); } return (int) position; } public bool insert_to_queue (Playlist playlist, uint position = -1, bool play_now = false) { var changed = merge_items_to_store (_music_queue, playlist.items, ref position); list_modified |= changed; if (play_now) { current_item = (int) position; _player.play (); } else if (changed) { update_current_item (); } return changed; } public async void load_music_folder_async () { var files = new File[] { File.new_for_uri (music_folder) }; var musics = new GenericArray (4096); yield _loader.load_files_async (files, musics, false, false, _sort_mode); _store_external_changed = true; if (_music_queue.get_n_items () == 0) { _music_queue.splice (0, _music_queue.get_n_items (), (Object[]) musics.data); } else { on_music_library_changed (0, 1, 1); } if (_current_music == null && _current_list.get_n_items () > 0) { current_item = 0; } else { update_current_item (); } } public async bool open_files_async (File[] files, uint position = -1, bool play_now = false) { var playlist = new Playlist (""); yield _loader.load_files_async (files, playlist.items); return playlist.length > 0 && insert_to_queue (playlist, position, play_now || _current_music == null); } public void play_next () { current_item++; _player.play (); } public void play_pause() { _player.playing = !_player.playing; } public void play_previous () { current_item--; _player.play (); } private bool _reloading = false; public void reload_library () { if (!_loading && !_reloading) { _reloading = true; var file = get_playing_list_file (); file.delete_async.begin (Priority.DEFAULT, null, (obj, res) => { try { file.delete_async.end (res); } catch (Error e) { } _loader.remove_all (); load_music_folder_async.begin ((obj, res) => { load_music_folder_async.end (res); _reloading = false; }); }); } } public void request_background () { var portal = _actions?.portal ?? new Portal (); portal.request_background_async.begin (_("Keep playing after window closed"), (obj, res) => portal.request_background_async.end (res)); } public async bool rename_playlist_async (Playlist playlist, string title) { var file = File.new_for_uri (playlist.list_uri); var uris = new GenericArray (playlist.length); playlist.items.foreach ((music) => uris.add (music.uri)); var saved = yield run_async (() => save_playlist_file (file, uris, title)); if (saved) { playlist.set_title (title); playlist_added (_loader.library.add_playlist (playlist)); } return saved; } public async bool save_queue_to_file () { var count = _music_queue.get_n_items (); var uris = new GenericArray (count); for (var i = 0; i < count; i++) { var music = (Music) _music_queue.get_item (i); uris.add (music.uri); } var file = get_playing_list_file (); var ret = yield run_async (() => save_playlist_file (file, uris, null, false), false, true); if (ret) _list_modified = false; return ret; } public async void save_to_playlist_file_async (Playlist playlist) { var uri = playlist.list_uri; var file = File.new_for_uri (uri); var append = uri.length == 0; if (append) { var filter = new Gtk.FileFilter (); filter.name = _("Playlist Files"); filter.add_mime_type ("audio/x-mpegurl"); filter.add_mime_type ("audio/x-scpls"); filter.add_mime_type ("public.m3u-playlist"); var initial = File.new_for_uri (music_folder).get_child (playlist.title + ".m3u"); var file_new = yield show_save_file_dialog (active_window, initial, {filter}); if (file_new == null) return; file = (!)file_new; playlist.set_list_uri (file.get_uri ()); playlist.set_title (get_file_display_name (file)); _settings.set_string ("recent-playlist", playlist.list_uri); } var saved = yield add_playlist_to_file_async (playlist, append); if (saved && append) Window.get_default ()?.show_toast (_("Save playlist successfully"), build_library_uri (null, playlist)); } public async void show_add_playlist_dialog (Playlist playlist) { var dialog = new PlaylistDialog (this); var pls = yield dialog.choose (Window.get_default ()); if (pls != null) { var list_uri = ((!)pls).list_uri; if (list_uri.length > 0) { playlist.set_list_uri (list_uri); yield add_playlist_to_file_async (playlist, true); } else { yield save_to_playlist_file_async (playlist); } } } private File? _cover_tmp_file = null; private async void delete_cover_tmp_file_async () { try { if (_cover_tmp_file != null) { yield ((!)_cover_tmp_file).delete_async (); _cover_tmp_file = null; } } catch (Error e) { } } private int find_music_item_by_uri (string uri) { var music = _loader.find_cache (uri); if (music != null) { var index = find_item_in_model (_current_list, music, _current_index); if (index != -1) return index; } var count = _current_list.get_n_items (); for (var i = 0; i < count; i++) { var m = (Music) _current_list.get_item (i); if (m.uri == uri) return (int) i; } return -1; } private void on_bus_acquired (DBusConnection connection, string name) { try { connection.register_object ("/org/mpris/MediaPlayer2", new MprisPlayer (this, connection)); connection.register_object ("/org/mpris/MediaPlayer2", new MprisRoot (this)); } catch (Error e) { warning ("Register MPRIS failed: %s\n", e.message); } } private void on_music_found (GenericArray arr) { _store_external_changed = true; if (arr.length > 0) { _music_queue.splice (_music_queue.get_n_items (), 0, (Object[]) arr.data); } else { on_music_library_changed (0, 1, 1); } } private uint _pending_mic_handler = 0; private uint _pending_msc_handler = 0; private void on_music_list_changed (uint position, uint removed, uint added) { if (removed != 0 || added != 0) { if (_pending_mic_handler != 0) Source.remove (_pending_mic_handler); _pending_mic_handler = run_idle_once (() => { _pending_mic_handler = 0; update_current_item (); }, Priority.LOW); } } private void on_music_library_changed (uint position, uint removed, uint added) { if (removed != 0 || added != 0) { if (_pending_msc_handler != 0) Source.remove (_pending_msc_handler); _pending_msc_handler = run_idle_once (() => { _pending_msc_handler = 0; music_library_changed (_store_external_changed); _store_external_changed = false; }, Priority.LOW); } } private void on_music_lost (GenericSet removed) { _store_external_changed = true; if (removed.length > 0) { var arr = new GenericArray (removed.length); removed.foreach ((music) => arr.add (music)); remove_items_from_store (_music_queue, arr); } else { on_music_library_changed (0, 1, 1); } } private void on_player_end () { if (_single_loop) { _player.seek (0); _player.play (); } else { current_item++; } } private void on_player_error (Error err) { Window.get_default ()?.show_toast (err.message); if (!_player.gapless) { on_player_end (); } } private string? on_player_next_uri_request () { // This is NOT called in main UI thread lock (_next_uri) { if (!_single_loop) _current_uri = _next_uri.str; // next_uri_start will be received soon later return _current_uri; } } private void on_player_next_uri_start () { // Received after next_uri_request on_player_end (); } private uint _inhibit_id = 0; private void on_player_state_changed (Gst.State state) { if (state == Gst.State.PLAYING && _inhibit_id == 0) { _inhibit_id = this.inhibit (Window.get_default (), Gtk.ApplicationInhibitFlags.SUSPEND, _("Keep playing")); } else if (state != Gst.State.PLAYING && _inhibit_id != 0) { this.uninhibit (_inhibit_id); _inhibit_id = 0; } } private int _cover_size = 360; private async void on_player_tag_parsed (string? u, Gst.TagList? tags) { var uri = u ?? ""; if (_current_music != null && _current_uri == uri) { var music = _loader.find_cache (_current_uri) ?? (!)_current_music; if (music != _current_music) { _current_music = music; music_changed (music); } else if (music.has_unknown () && tags != null && music.from_gst_tags ((!)tags)) { _loader.add_to_cache (music); music_changed (music); } _current_cover = tags != null ? parse_image_from_tag_list ((!)tags) : null; if (_current_cover == null && u != null) { _current_cover = yield run_async (() => { var file = File.new_for_uri ((!)uri); var t = parse_gst_tags (file); return t != null ? parse_image_from_tag_list ((!)t) : null; }); } Gdk.Pixbuf? pixbuf = null; var image = _current_cover; if (_current_uri == uri) { var size = _cover_size * _thumbnailer.scale_factor; if (image != null) { pixbuf = yield run_async ( () => load_clamp_pixbuf_from_sample ((!)image, size), true); } if (pixbuf == null) { pixbuf = yield _thumbnailer.load_directly_async (music, size); } } if (_current_uri == uri) { var cover_uri = music.cover_uri; if (cover_uri == null) { var dir = File.new_build_filename (Environment.get_user_cache_dir (), application_id); var name = Checksum.compute_for_string (ChecksumType.MD5, music.cover_key); var file = dir.get_child (name); cover_uri = file.get_uri (); if (image != null) { yield save_sample_to_file_async (file, (!)image); } else { var svg = _thumbnailer.create_music_text_svg (music); yield save_text_to_file_async (file, svg); } if (strcmp (cover_uri, _cover_tmp_file?.get_uri ()) != 0) { yield delete_cover_tmp_file_async (); _cover_tmp_file = file; } } if (_current_uri == uri) { music_cover_parsed (music, pixbuf, cover_uri); } } // Update thumbnail cache if remote thumbnail not loaded if (pixbuf != null && !(_thumbnailer.find (music) is Gdk.Texture)) { var minbuf = yield run_async ( () => create_clamp_pixbuf ((!)pixbuf, Thumbnailer.ICON_SIZE * _thumbnailer.scale_factor) ); if (minbuf != null) { var paintable = Gdk.Texture.for_pixbuf ((!)minbuf); _thumbnailer.put (music, paintable, true, Thumbnailer.ICON_SIZE); thumbnail_changed (music, paintable); } } } } private void update_current_item () { var index = find_item_in_model (_current_list, _current_music, _current_index); if (index == -1 && _current_music != null) { unowned var uri = ((!)_current_music).uri; index = find_music_item_by_uri (uri); current_music = index != -1 ? _current_list.get_item (index) as Music : _loader.find_cache (uri); } var size = _current_list.get_n_items (); if (_current_index != index || _current_list_size != size) { _current_index = index; _current_list_size = size; index_changed (index, size); } update_next_item (); } private void update_next_item () { var next_music = (Music?) _current_list.get_item (_current_index + 1); lock (_next_uri) { _next_uri.assign (next_music?.uri ?? ""); } } } public File get_playing_list_file () { var cache_dir = Environment.get_user_cache_dir (); return File.new_build_filename (cache_dir, Config.APP_ID, PageName.PLAYING + ".m3u"); } public async bool save_sample_to_file_async (File file, Gst.Sample sample) { var buffer = sample.get_buffer (); Gst.MapInfo? info = null; try { var stream = yield file.replace_async (null, false, FileCreateFlags.NONE); if (buffer?.map (out info, Gst.MapFlags.READ) ?? false) { return yield stream.write_all_async (info?.data, Priority.DEFAULT, null, null); } } catch (Error e) { } finally { if (info != null) buffer?.unmap ((!)info); } return false; } public async bool save_text_to_file_async (File file, string text) { try { var stream = yield file.replace_async (null, false, FileCreateFlags.NONE); unowned uint8[] data = (uint8[])text; var size = text.length; return yield stream.write_all_async (data[0:size], Priority.DEFAULT, null, null); } catch (Error e) { } return false; } } ================================================ FILE: src/config.vapi ================================================ [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] namespace Config { public const string APP_ID; public const string CODE_NAME; public const string VERSION; public const string LOCALEDIR; } [CCode (cprefix = "")] namespace GLib { [CCode (cname = "g_strndup")] public string strndup (char* str, size_t n); } ================================================ FILE: src/gresource.xml ================================================ gtk/style.css gtk/help-overlay.ui gtk/play-panel.ui gtk/preferences.ui gtk/store-panel.ui ================================================ FILE: src/gst/ape-demux.c ================================================ /* GStreamer APEv1/2 tag reader * Copyright (C) 2004 Ronald Bultje * Copyright (C) 2006 Tim-Philipp Müller * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:element-apedemux * @title: apedemux * * apedemux accepts data streams with APE tags at the start or at the end * (or both). The mime type of the data between the tag blocks is detected * using typefind functions, and the appropriate output mime type set on * outgoing buffers. * * The element is only able to read APE tags at the end of a stream from * a seekable stream, ie. when get_range mode is supported by the upstream * elements. If get_range operation is available, apedemux makes it available * downstream. This means that elements which require get_range mode, such as * wavparse or musepackdec, can operate on files containing APE tag * information. * * ## Example launch line * |[ * gst-launch-1.0 -t filesrc location=file.mpc ! apedemux ! fakesink * ]| This pipeline should read any available APE tag information and output it. * The contents of the file inside the APE tag regions should be detected, and * the appropriate mime type set on buffers produced from apedemux. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include static const struct _GstApeDemuxTagTableEntry { const gchar *ape_tag; const gchar *gst_tag; } tag_table[] = { { "replaygain_track_gain", GST_TAG_TRACK_GAIN}, { "replaygain_track_peak", GST_TAG_TRACK_PEAK}, { "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, { "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, { "title", GST_TAG_TITLE}, { "artist", GST_TAG_ARTIST}, { "album", GST_TAG_ALBUM}, { "composer", GST_TAG_COMPOSER}, { "comment", GST_TAG_COMMENT}, { "comments", GST_TAG_COMMENT}, { "copyright", GST_TAG_COPYRIGHT}, { "genre", GST_TAG_GENRE}, { "isrc", GST_TAG_ISRC}, { "disc", GST_TAG_ALBUM_VOLUME_NUMBER}, { "disk", GST_TAG_ALBUM_VOLUME_NUMBER}, { "discnumber", GST_TAG_ALBUM_VOLUME_NUMBER}, { "disknumber", GST_TAG_ALBUM_VOLUME_NUMBER}, { "track", GST_TAG_TRACK_NUMBER}, { "tracknumber", GST_TAG_TRACK_NUMBER}, { "year", GST_TAG_DATE}, { "file", GST_TAG_LOCATION} }; static gboolean ape_demux_get_gst_tag_from_tag (const gchar * ape_tag, const gchar ** gst_tag, GType * gst_tag_type) { gint i; for (i = 0; i < G_N_ELEMENTS (tag_table); ++i) { if (g_ascii_strcasecmp (tag_table[i].ape_tag, ape_tag) == 0) { *gst_tag = tag_table[i].gst_tag; *gst_tag_type = gst_tag_get_type (tag_table[i].gst_tag); GST_LOG ("Mapped APE tag '%s' to GStreamer tag '%s'", ape_tag, *gst_tag); return TRUE; } } GST_WARNING ("Could not map APE tag '%s' to a GStreamer tag", ape_tag); return FALSE; } GstTagList * ape_demux_parse_tags (const guint8 * data, gint size) { GstTagList *taglist = gst_tag_list_new_empty (); GST_LOG ("Reading tags from chunk of size %u bytes", size); /* get rid of header/footer */ if (size >= 32 && memcmp (data, "APETAGEX", 8) == 0) { data += 32; size -= 32; } if (size > 32 && memcmp (data + size - 32, "APETAGEX", 8) == 0) { size -= 32; } /* read actual tags - at least 10 bytes for tag header */ while (size >= 10) { guint len, n = 8; gchar *tag, *val; const gchar *gst_tag; GType gst_tag_type; /* find tag type and size */ len = GST_READ_UINT32_LE (data); while (n < size && data[n] != 0x0) n++; if (n == size) break; g_assert (data[n] == 0x0); n++; if (size - n < len) break; /* If the tag is empty, skip to the next one */ if (len == 0) goto next_tag; /* read */ tag = g_strndup ((gchar *) data + 8, n - 9); val = g_strndup ((gchar *) data + n, len); GST_LOG ("tag [%s], val[%s]", tag, val); /* special-case 'media' tag, could be e.g. "CD 1/2" */ if (g_ascii_strcasecmp (tag, "media") == 0) { gchar *sp, *sp2; g_free (tag); tag = g_strdup ("discnumber"); /* get rid of the medium in front */ sp = strchr (val, ' '); while (sp != NULL && (sp2 = strchr (sp + 1, ' ')) != NULL) sp = sp2; if (sp) { memmove (val, sp + 1, strlen (sp + 1) + 1); } } if (ape_demux_get_gst_tag_from_tag (tag, &gst_tag, &gst_tag_type)) { GValue v = { 0, }; switch (gst_tag_type) { case G_TYPE_INT:{ gint v_int; if (sscanf (val, "%d", &v_int) == 1) { g_value_init (&v, G_TYPE_INT); g_value_set_int (&v, v_int); } break; } case G_TYPE_UINT:{ guint v_uint, count; if (strcmp (gst_tag, GST_TAG_TRACK_NUMBER) == 0) { gint dummy; if (sscanf (val, "%u", &v_uint) == 1 && v_uint > 0) { g_value_init (&v, G_TYPE_UINT); g_value_set_uint (&v, v_uint); } GST_LOG ("checking for track count: %s", val); /* might be 0/N or -1/N to specify that there is only a count */ if (sscanf (val, "%d/%u", &dummy, &count) == 2 && count > 0) { gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_COUNT, count, NULL); } } else if (strcmp (gst_tag, GST_TAG_ALBUM_VOLUME_NUMBER) == 0) { gint dummy; if (sscanf (val, "%u", &v_uint) == 1 && v_uint > 0) { g_value_init (&v, G_TYPE_UINT); g_value_set_uint (&v, v_uint); } GST_LOG ("checking for volume count: %s", val); /* might be 0/N or -1/N to specify that there is only a count */ if (sscanf (val, "%d/%u", &dummy, &count) == 2 && count > 0) { gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_ALBUM_VOLUME_COUNT, count, NULL); } } else if (sscanf (val, "%u", &v_uint) == 1) { g_value_init (&v, G_TYPE_UINT); g_value_set_uint (&v, v_uint); } break; } case G_TYPE_STRING:{ g_value_init (&v, G_TYPE_STRING); g_value_set_string (&v, val); break; } case G_TYPE_DOUBLE:{ gdouble v_double; gchar *endptr; /* floating point strings can be "4,123" or "4.123" depending on * the locale. We need to be able to parse and read either version * no matter what our current locale is */ g_strdelimit (val, ",", '.'); v_double = g_ascii_strtod (val, &endptr); if (endptr != val) { g_value_init (&v, G_TYPE_DOUBLE); g_value_set_double (&v, v_double); } break; } default:{ if (gst_tag_type == G_TYPE_DATE) { gint v_int; if (sscanf (val, "%d", &v_int) == 1) { GDate *date = g_date_new_dmy (1, 1, v_int); g_value_init (&v, G_TYPE_DATE); g_value_take_boxed (&v, date); } } else { GST_WARNING ("Unhandled tag type '%s' for tag '%s'", g_type_name (gst_tag_type), gst_tag); } break; } } if (G_VALUE_TYPE (&v) != 0) { gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, gst_tag, &v, NULL); g_value_unset (&v); } } GST_DEBUG ("Read tag %s: %s", tag, val); g_free (tag); g_free (val); /* move data pointer */ next_tag: size -= len + n; data += len + n; } GST_DEBUG ("Taglist: %" GST_PTR_FORMAT, taglist); return taglist; } ================================================ FILE: src/gst/gst-ext.h ================================================ #ifndef __GST_EXT_H__ #define __GST_EXT_H__ #include G_BEGIN_DECLS GstTagList * ape_demux_parse_tags (const guint8 * data, gint size); G_END_DECLS #endif /* __GST_EXT_H__ */ ================================================ FILE: src/gst/gst-ext.vapi ================================================ [CCode (cprefix = "", lower_case_cprefix = "")] namespace GstExt { [CCode (cheader_filename = "gst-ext.h")] public static Gst.TagList ape_demux_parse_tags ([CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data); } ================================================ FILE: src/gst/gst-player.vala ================================================ namespace G4 { public class GstPlayer : Object { public static void init (ref unowned string[]? args) { Gst.init (ref args); } public static Gst.ClockTime from_second (double time) { return (Gst.ClockTime) (time * Gst.SECOND); } public static double to_second (Gst.ClockTime time) { return (double) time / Gst.SECOND; } public static void get_audio_sinks (GenericArray sinks) { var caps = new Gst.Caps.simple ("audio/x-raw", "format", Type.STRING, "S16LE", null); var list = Gst.ElementFactory.list_get_elements (Gst.ElementFactoryType.AUDIOVIDEO_SINKS, Gst.Rank.SECONDARY); list = Gst.ElementFactory.list_filter (list, caps, Gst.PadDirection.SINK, false); list.foreach ((factory) => sinks.add (factory)); } private dynamic Gst.Pipeline? _pipeline = null; private dynamic Gst.Element? _audio_sink = null; private dynamic Gst.Element? _replay_gain = null; private string _audio_sink_name = ""; private int _audio_sink_requested = 0; private string? _current_uri = null; private Gst.ClockTime _duration = Gst.CLOCK_TIME_NONE; private Gst.ClockTime _position = Gst.CLOCK_TIME_NONE; private int _last_error_code = 0; private ulong _about_to_finish_id = 0; private int _next_uri_requested = 0; private double _last_peak = 0; private LevelCalculator _peak_calculator = new LevelCalculator (); private Gst.State _state = Gst.State.NULL; private bool _seeking = false; private Gst.TagList? _tag_list = null; private uint _tag_handle = 0; private bool _tag_parsed = false; private uint _timer_handle = 0; private unowned Thread _main_thread = Thread.self (); public signal void duration_changed (Gst.ClockTime duration); public signal void error (Error error); public signal void end_of_stream (); public signal void position_updated (Gst.ClockTime position); public signal string? next_uri_request (); public signal void next_uri_start (); public signal void state_changed (Gst.State state); public signal void tag_parsed (string? uri, Gst.TagList? tags); public GstPlayer () { uint major = 0, minor = 0, micro = 0, nano = 0; Gst.version (out major, out minor, out micro, out nano); if (major > 1 || (major == 1 && minor >= 24)) { _pipeline = Gst.ElementFactory.make ("playbin3", "player") as Gst.Pipeline; if (_pipeline != null) print (@"Use playbin3\n"); } if (_pipeline == null) { _pipeline = Gst.ElementFactory.make ("playbin", "player") as Gst.Pipeline; } if (_pipeline != null) { var pipeline = (!)_pipeline; pipeline.async_handling = true; pipeline.flags = 0x0022; // audio | native audio pipeline.bind_property ("volume", this, "volume", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); pipeline.get_bus ().add_watch (Priority.DEFAULT, bus_callback); } else { critical ("Create playbin failed\n"); } } ~GstPlayer () { if (_tag_handle != 0) Source.remove (_tag_handle); if (_timer_handle != 0) Source.remove (_timer_handle); _peak_calculator.clear (); _pipeline?.set_state (Gst.State.NULL); } public bool gapless { get { return _about_to_finish_id != 0; } set { if (_pipeline != null) { var pipeline = (!)_pipeline; if (_about_to_finish_id != 0) { pipeline.disconnect (_about_to_finish_id); _about_to_finish_id = 0; } if (value) { _about_to_finish_id = pipeline.about_to_finish.connect (on_stream_to_finish); } } } } public bool playing { get { return _state == Gst.State.PLAYING; } set { state = _state == Gst.State.PLAYING ? Gst.State.PAUSED : Gst.State.PLAYING; } } public Gst.State state { get { return _state; } set { _pipeline?.set_state (value); } } public string? uri { get { return _current_uri; } set { _current_uri = value; if (_pipeline != null) { ((!)_pipeline).uri = value; if (AtomicInt.compare_and_exchange (ref _audio_sink_requested, 1, 0)) update_audio_sink (); } } } public string audio_sink { get { return _audio_sink_name; } set { var sink_name = value; if (sink_name.length == 0 || (Gst.ElementFactory.find (sink_name)?.get_rank () ?? Gst.Rank.NONE) <= Gst.Rank.MARGINAL) { var list = Gst.ElementFactory.list_get_elements (Gst.ElementFactoryType.AUDIOVIDEO_SINKS, Gst.Rank.PRIMARY); if (!list.is_empty ()) sink_name = list.first ().data.name; } var sink = Gst.ElementFactory.make (sink_name, sink_name); if (sink != null) { _audio_sink = sink; _audio_sink_name = value; ((!)_audio_sink).enable_last_sample = true; } if (_pipeline != null && _current_uri != null) update_audio_sink (); else AtomicInt.set (ref _audio_sink_requested, 1); } } public double peak { get { var value = _last_peak; dynamic Gst.Element? sink = _audio_sink ?? _pipeline?.audio_sink; if (sink != null) { dynamic Gst.Sample? sample = ((!)sink).last_sample; if (sample != null) _peak_calculator.calculate_sample ((!)sample, _position, ref value); } value = double.max (value, _last_peak >= 0.033 ? _last_peak - 0.033 : 0); _last_peak = value; return value; } } public Gst.ClockTime position { get { return _position; } set { seek (value); } } public uint replay_gain { get { if (_replay_gain != null) return ((!)_replay_gain).album_mode ? 2 : 1; return 0; } set { _replay_gain = value != 0 ? Gst.ElementFactory.make ("rgvolume", "gain") : null; if (_replay_gain != null) ((!)_replay_gain).album_mode = value == 2; if (_pipeline != null && _current_uri != null) update_audio_sink (); else AtomicInt.set (ref _audio_sink_requested, 1); } } public Gst.TagList? tag_list { get { return _tag_list; } } public double volume { get; set; } public void play () { _pipeline?.set_state (Gst.State.PLAYING); } public void pause () { _pipeline?.set_state (Gst.State.PAUSED); } public void seek (Gst.ClockTime position) { if (_pipeline != null && !_seeking) { // print ("Seek: %g -> %g\n", to_second (_position), to_second (position)); _seeking = ((!)_pipeline).seek_simple (Gst.Format.TIME, Gst.SeekFlags.ACCURATE | Gst.SeekFlags.FLUSH, (int64) position); } } private void emit_tag_parsed (uint delay = 0) { if (_tag_handle != 0) Source.remove (_tag_handle); _tag_handle = run_timeout_once (delay, () => { _tag_handle = 0; _tag_parsed = true; tag_parsed (_current_uri, _tag_list); }); } private bool bus_callback (Gst.Bus bus, Gst.Message message) { unowned Thread thread = Thread.self (); if (thread == _main_thread) { on_bus_message (message); } else { message.ref (); Idle.add (() => { on_bus_message (message); message.unref (); return false; }); warning ("Bus message not in main thread: %p, %s\n", thread, message.type.get_name ()); } return true; } private void on_bus_message (Gst.Message message) { switch (message.type) { case Gst.MessageType.ASYNC_DONE: parse_position (); _seeking = false; break; case Gst.MessageType.DURATION_CHANGED: parse_duration (); break; case Gst.MessageType.STATE_CHANGED: if (message.src == (!)_pipeline) { Gst.State old = Gst.State.NULL, state = Gst.State.NULL, pend = Gst.State.NULL; message.parse_state_changed (out old, out state, out pend); on_state_changed (old, state); } break; case Gst.MessageType.ERROR: Error err; string debug; message.parse_error (out err, out debug); print (@"Player error: $(err.domain), $(err.code), $(err.message)\n"); if (_last_error_code < err.code) { _last_error_code = err.code; error (err); } break; case Gst.MessageType.EOS: end_of_stream (); break; case Gst.MessageType.STREAM_START: on_stream_start (); if (!_tag_parsed) { emit_tag_parsed (50); } break; case Gst.MessageType.TAG: Gst.TagList? tags = null; message.parse_tag (out tags); _tag_list = merge_tags (_tag_list, tags); if (!_tag_parsed) { emit_tag_parsed (tags_has_image (_tag_list) ? 0 : 50); } break; default: break; } } private void on_state_changed (Gst.State old, Gst.State state) { if (old != state && _state != state) { _state = state; state_changed (state); // print (@"State changed: $old -> $state\n"); } if (_timer_handle == 0 && state == Gst.State.PLAYING) { _timer_handle = Timeout.add (100, parse_position); } else if (_timer_handle != 0 && state != Gst.State.PLAYING) { Source.remove (_timer_handle); _timer_handle = 0; } } private void on_stream_start () { _last_error_code = 0; _peak_calculator.clear (); _tag_list = null; _tag_parsed = false; if (AtomicInt.compare_and_exchange (ref _next_uri_requested, 1, 0)) { next_uri_start (); } parse_duration (); parse_position (); } private void on_stream_to_finish () { var next_uri = next_uri_request (); if (next_uri != null && ((!)next_uri).length > 0) { AtomicInt.set (ref _next_uri_requested, 1); uri = (!)next_uri; } } private void parse_duration () { if (((!)_pipeline).query_duration (Gst.Format.TIME, out _duration)) { duration_changed (_duration); } else { _duration = Gst.CLOCK_TIME_NONE; } } private bool parse_position () { if (((!)_pipeline).query_position (Gst.Format.TIME, out _position)) { position_updated (_position); } else { _position = Gst.CLOCK_TIME_NONE; } return true; } private Gst.Element? setup_audio_sink () { dynamic Gst.Bin? sink_bin = Gst.ElementFactory.make ("bin", "audio-sink-bin") as Gst.Bin; if (_replay_gain != null) { (_replay_gain?.parent as Gst.Bin)?.remove_element ((!)_replay_gain); sink_bin?.add ((!)_replay_gain); } print (@"Enable ReplayGain: $(_replay_gain != null)\n"); if (_audio_sink != null) { (_audio_sink?.parent as Gst.Bin)?.remove_element ((!)_audio_sink); sink_bin?.add ((!)_audio_sink); _replay_gain?.link ((!)_audio_sink); } print ("Audio Sink: %s\n", _audio_sink?.name ?? ""); Gst.Pad? static_pad = (_replay_gain ?? _audio_sink)?.get_static_pad ("sink"); if (static_pad != null) { sink_bin?.add_pad (new Gst.GhostPad ("sink", (!)static_pad)); } else { (_audio_sink?.parent as Gst.Bin)?.remove_element ((!)_audio_sink); } return static_pad != null ? sink_bin : _audio_sink; } private void update_audio_sink () { var pipeline = (!)_pipeline; var saved_pos = _position; var saved_state = _state; pipeline.set_state (Gst.State.NULL); pipeline.audio_sink = setup_audio_sink (); if (saved_state != Gst.State.NULL) { pipeline.set_state (saved_state); } if (saved_pos != Gst.CLOCK_TIME_NONE && saved_state >= Gst.State.PAUSED) { Idle.add (() => { if (_state == saved_state) { seek (saved_pos); return false; } return _state != Gst.State.NULL; }, Priority.LOW); } } } } ================================================ FILE: src/gst/peak-calculator.vala ================================================ namespace G4 { public class LevelCalculator { struct Peak { Gst.ClockTime time; double peak; } private int _audio_channels = 2; private int _audio_bps = 2; private int _sample_bps = 2; private unowned Gst.Caps? _last_caps = null; private unowned Gst.ClockTime _last_sample_time = Gst.CLOCK_TIME_NONE; private LevelCalculateFunc? _level_calculate = null; private Queue _peaks = new Queue (); public void clear () { _last_sample_time = Gst.CLOCK_TIME_NONE; _peaks.clear (); } public void calculate_sample (Gst.Sample sample, Gst.ClockTime position, ref double peak_value) { var peak = Peak (); peak.time = sample.get_segment ().position; if (_last_sample_time != peak.time && calculate_peak_in_sample (sample, out peak.peak)) { _peaks.push_tail (peak); _last_sample_time = peak.time; } while (_peaks.length > 0) { unowned var p = (!)_peaks.peek_head (); if (p.time >= position) { peak_value = p.peak; _peaks.pop_head (); } else { break; } } } private delegate void LevelCalculateFunc (uint8* data, uint num, uint channels, uint value_size, uint sample_size, out double nps); private bool calculate_peak_in_sample (Gst.Sample sample, out double peak) { peak = 0; unowned var caps = sample.get_caps (); if (_last_caps != caps || _level_calculate == null) { unowned var st = caps?.get_structure (0); st?.get_int ("channels", out _audio_channels); if (_audio_channels == 0) return false; unowned var format = st?.get_string ("format") ?? ""; switch (format) { case "S8": _audio_bps = _sample_bps = 1; _level_calculate = level_calculate_int; break; case "S16LE": _audio_bps = _sample_bps = 2; _level_calculate = level_calculate_int16; break; case "S24LE": _audio_bps = _sample_bps = 3; _level_calculate = level_calculate_int; break; case "S24_32LE": _audio_bps = 3; _sample_bps = 4; _level_calculate = level_calculate_int; break; case "S32LE": _audio_bps = _sample_bps = 4; _level_calculate = level_calculate_int; break; case "F32LE": _audio_bps = _sample_bps = 4; _level_calculate = level_calculate_float; break; case "F64LE": _audio_bps = _sample_bps = 8; _level_calculate = level_calculate_double; break; default: print ("Unsupported sample format: %s\n", format); return false; } _last_caps = caps; } var channels = _audio_channels; var bps = _audio_bps; var sample_size = _sample_bps; var block_size = channels * sample_size; var buffer = sample.get_buffer (); var size = buffer?.get_size () ?? 0; Gst.MapInfo? map_info = null; if (buffer?.map (out map_info, Gst.MapFlags.READ) ?? false) { unowned uint8* p = ((!)map_info).data; var num = (uint) (size / block_size); double total_nps = 0; for (var i = 0; i < channels; i++) { double nps = 0; _level_calculate (p + (sample_size * i), num, channels, bps, sample_size, out nps); total_nps += nps; } peak = double.min (total_nps / channels, 1); buffer?.unmap ((!)map_info); return true; } return false; } } void level_calculate_double (uint8* data, uint num, uint channels, uint value_size, uint sample_size, out double nps) { double peak = 0; double* p = (double*)data; for (uint i = 0; i < num; i += channels) { double value = p[i] >= 0 ? p[i] : -p[i]; if (peak < value) peak = value; } nps = peak * peak; } void level_calculate_float (uint8* data, uint num, uint channels, uint value_size, uint sample_size, out double nps) { float peak = 0f; float* p = (float*)data; for (uint i = 0; i < num; i += channels) { float value = p[i] >= 0 ? p[i] : -p[i]; if (peak < value) peak = value; } nps = (double) peak * peak; } void level_calculate_int16 (uint8* data, uint num, uint channels, uint value_size, uint sample_size, out double nps) { int16 peak = 0; int16* p = (int16*)data; for (uint i = 0; i < num; i += channels) { int16 value = p[i] >= 0 ? p[i] : -p[i]; if (peak < value) peak = value; } nps = (double) peak * peak / (((int64) 1) << (15 * 2)); } void level_calculate_int (uint8* data, uint num, uint channels, uint value_size, uint sample_size, out double nps) { int32 peak = 0; uint block_size = channels * sample_size; for (uint i = 0; i < num; i += channels) { int32 value = 0; uint8* p = (uint8*)&value + (4 - value_size); for (uint j = 0; j < value_size; j++) { p[j] = data[j]; } data += block_size; value = value >= 0 ? value : -value; if (peak < value) peak = value; } nps = (double) peak * peak / (((int64) 1) << (31 * 2)); } } ================================================ FILE: src/gst/tag-parser.vala ================================================ namespace G4 { public static Gst.TagList? parse_gst_tags (File file) { FileInputStream? fis = null; try { fis = file.read (); } catch (Error e) { return null; } Gst.TagList? tags = null; var stream = new BufferedInputStream ((!)fis); var head = new uint8[16]; // Parse and merge all the leading tags as possible while (true) { try { read_full (stream, head); // Try parse start tag: ID3v2 or APE if (Memory.cmp (head, "ID3", 3) == 0) { var buffer = Gst.Buffer.new_wrapped_full (0, head, 0, head.length, null); var size = Gst.Tag.get_id3v2_tag_size (buffer); if (size > head.length) { var data = new_uint8_array (size); Memory.copy (data, head, head.length); read_full (stream, data[head.length:]); var buffer2 = Gst.Buffer.new_wrapped_full (0, data, 0, data.length, null); var tags2 = Gst.Tag.List.from_id3v2_tag (buffer2); tags = merge_tags (tags, tags2); } } else if (Memory.cmp (head, "APETAGEX", 8) == 0) { seek_full (stream, 0, SeekType.SET); var size = read_uint32_le (head, 12) + 32; var data = new_uint8_array (size); Memory.copy (data, head, head.length); read_full (stream, data[head.length:]); var tags2 = GstExt.ape_demux_parse_tags (data); tags = merge_tags (tags, tags2); } else { // Parse by file container format if (Memory.cmp (head, "fLaC", 4) == 0) { seek_full (stream, - head.length); var tags2 = parse_flac_tags (stream); tags = merge_tags (tags, tags2); } else if (Memory.cmp (head, "OggS", 4) == 0) { seek_full (stream, - head.length); var tags2 = parse_ogg_tags (stream); tags = merge_tags (tags, tags2); } else if (Memory.cmp (head + 4, "ftyp", 4) == 0) { seek_full (stream, - head.length); var tags2 = parse_mp4_tags (stream); tags = merge_tags (tags, tags2); } // No ID3v2/APE any more, quit the loop break; } } catch (Error e) { print ("Parse begin tag %s: %s\n", file.get_parse_name (), e.message); break; } } if (tags_has_text (tags) || tags_has_image (tags)) { return tags; } // Parse and merge all the ending tags as possible try { var tags2 = parse_end_tags (stream); tags = merge_tags (tags, tags2); } catch (Error e) { print ("Parse end tag %s: %s\n", file.get_parse_name (), e.message); } if (tags != null) { // Fast parsing is done, just return return tags; } // Parse tags by Gstreamer demux/parse, it is slow try { var demux_name = get_demux_name_by_content (head); if (demux_name != null) { seek_full (stream, 0, SeekType.SET); tags = parse_demux_tags (stream, (!)demux_name); } } catch (Error e) { // print ("Parse demux %s: %s\n", file.get_parse_name (), e.message); } return tags; } public static bool tags_has_image (Gst.TagList? tags) { if (tags == null) return false; var t = (!)tags; return t.get_tag_size (Gst.Tags.IMAGE) > 0 || t.get_tag_size (Gst.Tags.PREVIEW_IMAGE) > 0; } public static bool tags_has_text (Gst.TagList? tags) { if (tags == null) return false; var t = (!)tags; return t.get_tag_size (Gst.Tags.ARTIST) > 0 || t.get_tag_size (Gst.Tags.TITLE) > 0; } public inline uint8[] new_uint8_array (uint size) throws Error { if ((int) size <= 0 || size > 0xfffffff) // 28 bits throw new IOError.INVALID_ARGUMENT ("invalid size"); return new uint8[size]; } public inline void read_full (BufferedInputStream stream, uint8[] buffer) throws Error { size_t bytes = 0; if (! stream.read_all (buffer, out bytes) || bytes != buffer.length) throw new IOError.FAILED ("read_all"); } public inline void seek_full (BufferedInputStream stream, int64 offset, SeekType type = SeekType.CUR) throws Error { if (! stream.seek (offset, type)) throw new IOError.FAILED ("seek"); } public inline uint32 read_uint32_be (uint8[] data, uint pos = 0) { return data[pos + 3] | ((uint32) (data[pos+2]) << 8) | ((uint32) (data[pos+1]) << 16) | ((uint32) (data[pos]) << 24); } public inline uint32 read_uint32_le (uint8[] data, uint pos = 0) { return data[pos] | ((uint32) (data[pos+1]) << 8) | ((uint32) (data[pos+2]) << 16) | ((uint32) (data[pos+3]) << 24); } public inline uint32 read_decimal_uint (uint8[] data) { uint32 n = 0; for (var i = 0; i < data.length; i++) { n = n * 10 + (data[i] - '0'); } return n; } public static Gst.TagList? merge_tags (Gst.TagList? tags, Gst.TagList? tags2, Gst.TagMergeMode mode = Gst.TagMergeMode.KEEP) { return tags != null ? tags?.merge (tags2, mode) : tags2; } public static Gst.TagList? parse_end_tags (BufferedInputStream stream) throws Error { var apev2_found = false; var foot = new uint8[128]; Gst.TagList? tags = null; seek_full (stream, 0, SeekType.END); while (true) { try { // Try parse end tag: ID3v1 or APE seek_full (stream, -128); read_full (stream, foot); if (Memory.cmp (foot, "TAG", 3) == 0) { var tags2 = Gst.Tag.List.new_from_id3v1 (foot); tags = merge_tags (tags, tags2); // print ("ID3v1 parsed: %d\n", tags2.n_tags ()); seek_full (stream, -128); } else if (Memory.cmp (foot[128-32:], "APETAGEX", 8) == 0) { var size = read_uint32_le (foot, 128 - 32 + 12) + 32; seek_full (stream, - (int) size); var data = new_uint8_array (size); read_full (stream, data); var tags2 = GstExt.ape_demux_parse_tags (data); // APEv2 is better than others, do REPLACE merge tags = merge_tags (tags, tags2, Gst.TagMergeMode.REPLACE); apev2_found = ! tags2.is_empty (); // print ("APEv2 parsed: %d\n", tags2.n_tags ()); seek_full (stream, - (int) size); } else if (Memory.cmp (foot[128-9:], "LYRICS200", 9) == 0) { var size = read_decimal_uint (foot[128-15:128-9]); seek_full (stream, - (int) (size + 15)); var data = new_uint8_array (size); read_full (stream, data); var tags2 = parse_lyrics200_tags (data); tags = merge_tags (tags, tags2, apev2_found ? Gst.TagMergeMode.KEEP : Gst.TagMergeMode.REPLACE); // print ("LYRICS200 parsed: %d\n", tags2.n_tags ()); seek_full (stream, - (int) (size + 15)); } else { break; } } catch (Error e) { if (tags == null) throw e; break; } } return tags; } public static Gst.TagList? parse_flac_tags (BufferedInputStream stream) throws Error { var head = new uint8[4]; read_full (stream, head); if (Memory.cmp (head, "fLaC", 4) != 0) { return null; } int flags = 0; Gst.TagList? tags = null; do { try { read_full (stream, head); var type = head[0] & 0x7f; var size = ((uint32) (head[1]) << 16) | ((uint32) (head[2]) << 8) | head[3]; // print ("FLAC block: %d, %u\n", type, size); if (type == 4) { var data = new_uint8_array (size + 4); read_full (stream, data[4:]); head[0] &= (~0x80); // clear the is-last flag Memory.copy (data, head, 4); var tags2 = Gst.Tag.List.from_vorbiscomment (data, head, null); tags = merge_tags (tags, tags2); flags |= 0x01; } else if (type == 6) { var data = new_uint8_array (size); read_full (stream, data); uint pos = 0; var img_type = read_uint32_be (data, pos); pos += 4; var img_mimetype_len = read_uint32_be (data, pos); pos += 4 + img_mimetype_len; if (pos + 4 > size) { break; } var img_description_len = read_uint32_be (data, pos); pos += 4 + img_description_len; pos += 4 * 4; // image properties if (pos + 4 > size) { break; } var img_len = read_uint32_be (data, pos); pos += 4; if (pos + img_len > size) { break; } tags = tags ?? new Gst.TagList.empty (); Gst.Tag.List.add_id3_image ((!)tags, data[pos:pos+img_len], img_type); flags |= 0x02; } else { seek_full (stream, size); } } catch (Error e) { if (tags == null) throw e; break; } } while ((flags & 0x03) != 0x03); return tags; } public const string[] ID3_TAG_ENCODINGS = { "GST_ID3V1_TAG_ENCODING", "GST_ID3_TAG_ENCODING", "GST_TAG_ENCODING", (string) null }; public static Gst.TagList parse_lyrics200_tags (uint8[] data) { var tags = new Gst.TagList.empty (); if (Memory.cmp (data, "LYRICSBEGIN", 11) != 0) { return tags; } var length = data.length; var pos = 11; while (pos + 8 < length) { var id = data[pos:pos+3]; pos += 3; var len = read_decimal_uint (data[pos:pos+5]); pos += 5; if (pos + len > length) { break; } var str = data[pos:pos+len]; pos += (int) len; string? tag = null; if (Memory.cmp (id, "EAL", 3) == 0) { tag = Gst.Tags.ALBUM; } else if (Memory.cmp (id, "EAR", 3) == 0) { tag = Gst.Tags.ARTIST; } else if (Memory.cmp (id, "ETT", 3) == 0) { tag = Gst.Tags.TITLE; } else if (Memory.cmp (id, "LYR", 3) == 0) { tag = Gst.Tags.LYRICS; } if (tag != null) { var value = Gst.Tag.freeform_string_to_utf8 ((char[]) str, ID3_TAG_ENCODINGS); if (value != (string)null && value.length > 0) { tags.add (Gst.TagMergeMode.REPLACE, (!)tag, value); } } } return tags; } private static void parse_mp4_date_value (uint8[] data, string tag, Gst.TagList tags) throws Error { var str = parse_mp4_string (data); if (str != null) { var date = new Gst.DateTime.from_iso8601_string ((!)str); tags.add (Gst.TagMergeMode.REPLACE, tag, date); // print (@"Tag: $tag=$(date.get_year ())\n"); } else { print ("MP4: unknown date type: %s\n", str ?? ""); } } private static void parse_mp4_image_value (uint8[] data, string tag, Gst.TagList tags) throws Error { var len = data.length; if (len > 16) { var type = read_uint32_be (data, 8); if ((type == 0x0000000d || type == 0x0000000e)) { var image_data = data[16:len]; Gst.Tag.ImageType image_type; if (tags.get_tag_size (Gst.Tags.IMAGE) == 0) image_type = Gst.Tag.ImageType.FRONT_COVER; else image_type = Gst.Tag.ImageType.NONE; var sample = Gst.Tag.image_data_to_image_sample (image_data, image_type); if (sample != (Gst.Sample)null) { tags.add (Gst.TagMergeMode.REPLACE, tag, sample); } // print (@"Tag: $tag=$(data.length)\n"); } } else { print ("MP4: unknown image type\n"); } } private static void parse_mp4_number_value (uint8[] data, string tag1, string tag2, Gst.TagList tags) throws Error { var len = data.length; if (len >= 22) { var type = read_uint32_be (data, 8); if (type == 0x00000000) { var n = read_uint32_be (data, 18); var n1 = n >> 16; var n2 = n & 0xffff; if (n1 > 0) { tags.add (Gst.TagMergeMode.REPLACE, tag1, n1); // print (@"Tag: $tag1=$n1\n"); } if (n2 > 0) { tags.add (Gst.TagMergeMode.REPLACE, tag2, n2); // print (@"Tag: $tag2=$n2\n"); } } } else { print ("MP4: unknown number type\n"); } } public const string[] MP4_TAG_ENCODINGS = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", (string) null }; private static string? parse_mp4_string (uint8[] data) throws Error { var len = data.length; if (len > 16) { var type = read_uint32_be (data, 8); if (type == 0x00000001) { var str_data = data[16:len]; return (string?) Gst.Tag.freeform_string_to_utf8 ((char[]) str_data, MP4_TAG_ENCODINGS); } } return null; } private static void parse_mp4_string_value (uint8[] data, string tag, Gst.TagList tags) throws Error { var str = parse_mp4_string (data); if (str != null && ((!)str).length > 0) { var value = (!)str; tags.add (Gst.TagMergeMode.REPLACE, tag, value); // print (@"Tag: $tag=$value\n"); } else { print ("MP4: unknown string type\n"); } } private static uint find_mp4_data_child (uint8[] buffer, uint pos, uint end, uint32 fourcc) { while (pos + 8 < end) { var box_size = read_uint32_be (buffer, pos); var box_type = read_uint32_be (buffer, pos + 4); if (box_type == fourcc) { return pos; } pos += box_size; } return end; } private static void parse_mp4_ilst_box (uint8[] buffer, Gst.TagList tags) throws Error { var size = buffer.length; uint pos = 0; while (pos + 8 < size) { var box_size = read_uint32_be (buffer, pos); var box_type = read_uint32_be (buffer, pos + 4); var box_end = pos + box_size; var data_pos = find_mp4_data_child (buffer, pos + 8, box_end, 0x64617461); // data if (box_size > 16 && data_pos != box_end) { var data_size = read_uint32_be (buffer, data_pos); var data = buffer[data_pos : data_pos + data_size]; switch (box_type) { case 0xa96e616du: // _nam case 0x7469746cu: // titl parse_mp4_string_value (data, Gst.Tags.TITLE, tags); break; case 0xa9415254u: // _ART case 0x70657266u: // perf parse_mp4_string_value (data, Gst.Tags.ARTIST, tags); break; case 0xa9616c62u: // _alb case 0x616c626du: // albm parse_mp4_string_value (data, Gst.Tags.ALBUM, tags); break; case 0xa9777274u: // _wrt case 0x61757468u: // auth parse_mp4_string_value (data, Gst.Tags.COMPOSER, tags); break; case 0xa9636d74u: // _cmt case 0xa9696e66u: // _inf parse_mp4_string_value (data, Gst.Tags.COMMENT, tags); break; case 0xa96c7972u: // _lyr parse_mp4_string_value (data, Gst.Tags.LYRICS, tags); break; case 0xa9646179u: // _day case 0x79727263u: // yrrc parse_mp4_date_value (data, Gst.Tags.DATE_TIME, tags); break; case 0x636f7672u: // covr parse_mp4_image_value (data, Gst.Tags.IMAGE, tags); break; case 0x64697363u: // disc case 0x6469736bu: // disk parse_mp4_number_value (data, Gst.Tags.ALBUM_VOLUME_NUMBER, Gst.Tags.ALBUM_VOLUME_COUNT, tags); break; case 0x74726b6eu: // trkn parse_mp4_number_value (data, Gst.Tags.TRACK_NUMBER, Gst.Tags.TRACK_COUNT, tags); break; default: break; } } pos = box_end; } } private static void parse_mp4_box (BufferedInputStream stream, Gst.TagList tags) throws Error { var box_head = new uint8[8]; while (tags.is_empty ()) { read_full (stream, box_head); var box_size = read_uint32_be (box_head); var box_type = read_uint32_be (box_head, 4); if (box_size <= 8) { continue; } else if (box_type == 0x6d6f6f76) { // moov parse_mp4_box (stream, tags); } else if (box_type == 0x75647461) { // udta parse_mp4_box (stream, tags); } else if (box_type == 0x6d657461 && box_size > 16) { // meta read_full (stream, box_head); var v1 = read_uint32_be (box_head); var v2 = read_uint32_be (box_head, 4); if (v2 == 0x68646c72) { // hdlr seek_full (stream, -8); parse_mp4_box (stream, tags); } else if (v1 == 0) { seek_full (stream, -4); parse_mp4_box (stream, tags); } } else if (box_type == 0x696c7374) { // ilst var buffer = new_uint8_array (box_size - 8); read_full (stream, buffer); parse_mp4_ilst_box (buffer, tags); } else { seek_full (stream, box_size - 8); } } } public static Gst.TagList? parse_mp4_tags (BufferedInputStream stream) { var tags = new Gst.TagList.empty (); try { parse_mp4_box (stream, tags); } catch (Error e) { print ("Parse MP4 Error %s\n", e.message); } return tags.is_empty () ? (Gst.TagList?) null : tags; } public const string[] OGG_TAG_IDS = { "\x03vorbis", "\x81kate\0\0\0\0", // "\x81daala", "\x81theora", "OpusTags", "OVP80\x02 ", }; public static Gst.TagList? parse_ogg_tags (BufferedInputStream stream) throws Error { var head = new uint8[27]; var mos = new MemoryOutputStream (null); while (true) { read_full (stream, head); uint seg_count = head[26]; var last_page = seg_count == 0; if (seg_count > 0) { var segments = new_uint8_array (seg_count); read_full (stream, segments); uint page_size = 0; for (var i = 0; i < seg_count; i++) { page_size += segments[i]; } var buffer = new_uint8_array (page_size); read_full (stream, buffer); mos.write (buffer); last_page = segments[seg_count - 1] != 255; } size_t size = 0; if (last_page && (size = mos.get_data_size ()) > 0) { var data = mos.get_data ()[0:size]; foreach (var id in OGG_TAG_IDS) { if (size > id.length && Memory.cmp (data, id, id.length) == 0) { return Gst.Tag.List.from_vorbiscomment (data, id.data, null); } } mos = new MemoryOutputStream (null); } if (seg_count == 0) { break; } } return null; } public static Gst.TagList? parse_demux_tags (BufferedInputStream stream, string demux_name) throws Error { var str = @"giostreamsrc name=src ! $(demux_name) ! fakesink sync=false"; dynamic Gst.Pipeline? pipeline = Gst.parse_launch (str) as Gst.Pipeline; dynamic Gst.Element? src = pipeline?.get_by_name ("src"); ((!)src).stream = stream; if (pipeline?.set_state (Gst.State.PLAYING) == Gst.StateChangeReturn.FAILURE) { throw new UriError.FAILED ("change state failed"); } var bus = pipeline?.get_bus (); bool quit = false; Gst.TagList? tags = null; do { var message = bus?.timed_pop (Gst.SECOND * 5); if (message == null) break; var msg = (!)message; switch (msg.type) { case Gst.MessageType.TAG: if (tags != null) { Gst.TagList? tags2 = null; msg.parse_tag (out tags2); tags = merge_tags (tags, tags2); } else { msg.parse_tag (out tags); } if (tags_has_text (tags) || tags_has_image (tags)) { quit = true; } break; case Gst.MessageType.ERROR: Error err; string debug; ((!)msg).parse_error (out err, out debug); print ("Parse error: %s, %s\n", err.message, debug); quit = true; break; case Gst.MessageType.EOS: quit = true; break; default: break; } } while (!quit); pipeline?.set_state (Gst.State.NULL); return tags; } private static string? get_demux_name_by_content (uint8[] head) { uint8* p = head; if (Memory.cmp (p, "FORM", 4) == 0 && (Memory.cmp (p + 8, "AIFF", 4) == 0 || Memory.cmp (p + 8, "AIFC", 4) == 0)) { return "aiffparse"; // } else if (Memory.cmp (p, "fLaC", 4) == 0) { // return "flacparse"; // } else if (Memory.cmp (p + 4, "ftyp", 4) == 0) { // return "qtdemux"; // } else if (Memory.cmp (p, "OggS", 4) == 0) { // return "oggdemux"; } else if (read_uint32_be (head) == 0x1A45DFA3) { // EBML_HEADER return "matroskademux"; } else if (Memory.cmp (p, "RIFF", 4) == 0 && Memory.cmp (p + 8, "WAVE", 4) == 0) { return "wavparse"; } else if (Memory.cmp (p, "\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16) == 0) { return "asfparse"; } return null; } public static void get_one_tag (Gst.TagList tags, string tag, GenericArray values) { var size = tags.get_tag_size (tag); for (var i = 0; i < size; i++) { var val = tags.get_value_index (tag, i); if (val != null) { var value = (!)val; if (value.holds (typeof (string))) { values.add (value.get_string ()); } else if (value.holds (typeof (uint))) { values.add (value.get_uint ().to_string ()); } else if (value.holds (typeof (double))) { values.add (value.get_double ().to_string ()); } else if (value.holds (typeof (bool))) { values.add (value.get_boolean ().to_string ()); } else if (value.holds (typeof (Gst.DateTime))) { var date = (Gst.DateTime) value.get_boxed (); values.add (date.to_iso8601_string () ?? ""); } } } } public static Gst.Sample? parse_image_from_tag_list (Gst.TagList tags) { Gst.Sample? sample = null; if (tags.get_sample (Gst.Tags.IMAGE, out sample)) { return sample; } if (tags.get_sample (Gst.Tags.PREVIEW_IMAGE, out sample)) { return sample; } for (var i = 0; i < tags.n_tags (); i++) { var tag = tags.nth_tag_name (i); var value = tags.get_value_index (tag, 0); sample = null; if (value?.type () == typeof (Gst.Sample) && tags.get_sample (tag, out sample)) { var caps = sample?.get_caps (); if (caps != null) { return sample; } // print (@"unknown image tag: $(tag)\n"); } } return null; } } ================================================ FILE: src/gtk/help-overlay.ui ================================================ true shortcuts 10 General Show Shortcuts win.show-help-overlay Preferences app.preferences Quit app.quit Show Tags app.show-cur-tags Playback Play/Pause app.play-pause Play Next app.next Play Previous app.prev Playlist Search win.toggle-search Toggle Sort app.toggle-sort Reload Library app.reload Save List win.save-list ================================================ FILE: src/gtk/play-panel.ui ================================================ ================================================ FILE: src/gtk/preferences.ui ================================================ ================================================ FILE: src/gtk/store-panel.ui ================================================ _Color Scheme _System app.scheme 0 _Light app.scheme 1 _Dark app.scheme 4
_Reload Library app.reload _Multiple Select win.select _Save List win.save-list
_Preferences app.preferences _Keyboard Shortcuts win.show-help-overlay _About app.about
Album app.sort 0 Artist app.sort 1 Artist/Album app.sort 2 Title app.sort 3 Recent app.sort 4 Shuffle app.sort 5
================================================ FILE: src/gtk/style.css ================================================ .title-large { font-size: 115%; font-weight: 550; } .title-leading { font-weight: 500; } .title-secondly { font-size: 92.5%; } gridview child { margin: 2px; padding: 0px; } searchbar revealer box { box-shadow: none; background-color: transparent; } tabbar revealer box { box-shadow: none; background-color: transparent; } ================================================ FILE: src/main.vala ================================================ /* * Copyright 2022 Nanling * * 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 . */ int main (string[] args) { Intl.bindtextdomain (Config.CODE_NAME, Config.LOCALEDIR); Intl.bind_textdomain_codeset (Config.CODE_NAME, "UTF-8"); Intl.textdomain (Config.CODE_NAME); Environment.set_prgname (Config.APP_ID); Environment.set_application_name (_("Gapless")); fix_gst_tag_encoding (); Random.set_seed ((uint32) get_monotonic_time ()); G4.GstPlayer.init (ref args); var app = new G4.Application (); return app.run (args); } void fix_gst_tag_encoding () { unowned var encoding = Environment.get_variable ("GST_TAG_ENCODING"); unowned var lang = Environment.get_variable ("LANG"); if (encoding == null && lang != null) { string[] lang_encodings = { "ja", "Shift_JIS", "ko", "EUC-KR", "zh_CN", "GB18030", "zh_HK", "BIG5HKSCS", "zh_SG", "GB2312", "zh_TW", "BIG5", }; for (var i = 0; i < lang_encodings.length; i += 2) { if (((!)lang).has_prefix (lang_encodings[i])) { Environment.set_variable ("GST_TAG_ENCODING", lang_encodings[i + 1], true); print ("Fix tag encoding: %s\n", lang_encodings[i + 1]); break; } } } } ================================================ FILE: src/meson.build ================================================ sources = [ 'action-handles.vala', 'application.vala', 'config.vapi', 'main.vala', 'gst/ape-demux.c', 'gst/gst-player.vala', 'gst/peak-calculator.vala', 'gst/tag-parser.vala', 'ui/dialogs.vala', 'ui/leaflet.vala', 'ui/mini-bar.vala', 'ui/music-list.vala', 'ui/music-widgets.vala', 'ui/narrow-bar.vala', 'ui/paintables.vala', 'ui/peak-bar.vala', 'ui/play-bar.vala', 'ui/play-panel.vala', 'ui/playlist-dialog.vala', 'ui/preferences.vala', 'ui/stable-label.vala', 'ui/store-panel.vala', 'ui/taglist-dialog.vala', 'ui/volume-button.vala', 'ui/window.vala', 'utils/async-task.vala', 'utils/cover-cache.vala', 'utils/data-io.vala', 'utils/dir-cache.vala', 'utils/dir-monitor.vala', 'utils/mpris.vala', 'utils/music.vala', 'utils/music-library.vala', 'utils/music-loader.vala', 'utils/playlist-file.vala', 'utils/portal.vala', 'utils/tag-cache.vala', 'utils/thumbnailer.vala', ] conf = configuration_data() conf.set_quoted('APP_ID', app_id) conf.set_quoted('CODE_NAME', meson.project_name()) conf.set_quoted('VERSION', version) conf.set_quoted('LOCALEDIR', get_option('prefix') / get_option('localedir')) configure_file(output: 'config.h', configuration: conf) c_name = meson.project_name() res_name = c_name + '-resources' sources += gnome.compile_resources(res_name, 'gresource.xml', c_name: c_name ) add_project_arguments('-I' + meson.current_source_dir () + '/gst/', language: 'c') executable(c_name, sources, vala_args: [ # meson.current_source_dir () + '/config.vapi', meson.current_source_dir () + '/gst/gst-ext.vapi', ], dependencies: dependencies, install: true, ) ================================================ FILE: src/ui/dialogs.vala ================================================ namespace G4 { #if ADW_1_5 public class Dialog : Adw.Dialog { public new void present (Gtk.Widget? parent) { content_width = compute_dialog_width (parent); base.present (parent); } #else public class Dialog : Gtk.Window { public override bool close_request () { closed (); return false; } public new void present (Gtk.Window? parent = null) { if (parent != null) { modal = true; transient_for = (!)parent; } set_titlebar (new Adw.Bin ()); width_request = compute_dialog_width (parent); base.present (); } public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { child.measure (orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); if (orientation == Gtk.Orientation.VERTICAL) { var height = transient_for.get_height (); if (natural > height && height > 0) natural = height; } } public virtual signal void closed () { } #endif } public int compute_dialog_width (Gtk.Widget? parent) { var width = parent?.get_width () ?? ContentWidth.MIN; if (width > 360) width = (width * 3 / 8).clamp (360, ContentWidth.MAX); return width; } public void show_about_dialog (Application app) { string[] authors = { "Nanling" }; /* Translators: Replace "translator-credits" with your names, one name per line */ var translator_credits = _("translator-credits"); var website = "https://gitlab.gnome.org/neithern/g4music"; var parent = Window.get_default (); #if ADW_1_5 var win = new Adw.AboutDialog (); run_idle_once (() => { if (parent != null && ((!)parent).get_width () < win.width_request) ((!)parent).default_width = win.width_request; }); #elif ADW_1_2 var win = new Adw.AboutWindow (); #endif #if ADW_1_2 win.application_icon = app.application_id; win.application_name = app.name; win.version = Config.VERSION; win.license_type = Gtk.License.GPL_3_0; win.developers = authors; win.website = website; win.issue_url = "https://gitlab.gnome.org/neithern/g4music/issues"; win.translator_credits = translator_credits; #if ADW_1_5 win.present (parent); #else if (parent != null) win.transient_for = (!)parent; win.present (); #endif #else Gtk.show_about_dialog (parent, "logo-icon-name", app.application_id, "program-name", app.name, "version", Config.VERSION, // "comments", comments, "authors", authors, "translator-credits", translator_credits, "license-type", Gtk.License.GPL_3_0, "website", website ); #endif } public async bool show_alert_dialog (string text, Gtk.Window? parent = null) { #if ADW_1_5 var result = false; var dialog = new Adw.AlertDialog (null, text); dialog.add_response ("no", _("No")); dialog.add_response ("yes", _("Yes")); dialog.default_response = "yes"; dialog.response.connect ((id) => { result = id == "yes"; Idle.add (show_alert_dialog.callback); }); dialog.present (parent); yield; return result; #elif GTK_4_10 var dialog = new Gtk.AlertDialog (text); dialog.buttons = { _("No"), _("Yes") }; dialog.cancel_button = 0; dialog.default_button = 1; dialog.modal = true; try { var btn = yield dialog.choose (parent, null); return btn == 1; } catch (Error e) { } return false; #else var result = -1; var dialog = new Gtk.MessageDialog (parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, text); dialog.response.connect ((id) => { dialog.destroy (); result = id; Idle.add (show_alert_dialog.callback); }); dialog.set_titlebar (new Adw.Bin ()); dialog.present (); yield; return result == Gtk.ResponseType.YES; #endif } public async File? show_save_file_dialog (Gtk.Window? parent, File? initial = null, Gtk.FileFilter[]? filters = null) { Gtk.FileFilter? default_filter = filters != null && ((!)filters).length > 0 ? ((!)filters)[0] : (Gtk.FileFilter?) null; #if GTK_4_10 var filter_list = new ListStore (typeof (Gtk.FileFilter)); if (filters != null) { foreach (var filter in (!)filters) filter_list.append (filter); } var dialog = new Gtk.FileDialog (); dialog.filters = filter_list; dialog.modal = true; dialog.set_default_filter (default_filter); dialog.set_initial_file (initial); try { return yield dialog.save (parent, null); } catch (Error e) { } return null; #else File? result = null; var chooser = new Gtk.FileChooserNative (null, parent, Gtk.FileChooserAction.SAVE, null, null); chooser.modal = true; try { var folder = initial?.get_parent (); if (folder != null) chooser.set_current_folder ((!)folder); chooser.set_current_name (initial?.get_basename () ?? ""); } catch (Error e) { } if (filters != null) { foreach (var filter in (!)filters) chooser.add_filter (filter); if (default_filter != null) chooser.set_filter ((!)default_filter); } chooser.response.connect ((id) => { var file = chooser.get_file (); if (id == Gtk.ResponseType.ACCEPT && file is File) { result = file; Idle.add (show_save_file_dialog.callback); } }); chooser.show (); yield; return result; #endif } public async File? show_select_folder_dialog (Gtk.Window? parent, File? initial = null) { #if GTK_4_10 var dialog = new Gtk.FileDialog (); dialog.set_initial_folder (initial); dialog.modal = true; try { return yield dialog.select_folder (parent, null); } catch (Error e) { } return null; #else File? result = null; var chooser = new Gtk.FileChooserNative (null, parent, Gtk.FileChooserAction.SELECT_FOLDER, null, null); try { if (initial != null) chooser.set_file ((!)initial); } catch (Error e) { } chooser.modal = true; chooser.response.connect ((id) => { if (id == Gtk.ResponseType.ACCEPT) { result = chooser.get_file (); Idle.add (show_select_folder_dialog.callback); } }); chooser.show (); yield; return result; #endif } } ================================================ FILE: src/ui/leaflet.vala ================================================ namespace G4 { namespace ContentWidth { public const int MIN = 340; public const int MAX = 480; } namespace LeafletMode { public const int SIDEBAR = 1; public const int CONTENT = 2; } public interface SizeWatcher { public abstract void first_allocated (); public abstract void size_to_change (int width, int height); } public class Leaflet : Gtk.Widget { private Gtk.Widget _content_box; private Gtk.Widget _sidebar_box; private Gtk.Widget _content = new Adw.Bin (); private Gtk.Widget _sidebar = new Adw.Bin (); private Stack _widget = new Stack (true); private bool _folded = false; private float _content_fraction = 3/8f; private int _content_min_width = ContentWidth.MIN; private int _content_max_width = ContentWidth.MAX; private int _view_width = 0; private int _view_height = 0; private int _visible_mode = LeafletMode.SIDEBAR; public Leaflet () { _sidebar_box = _widget.add (_sidebar, "sidebar"); _content_box = _widget.add (_content, "content"); _widget.widget.set_parent (this); _widget.notify["visible-child"].connect (() => { var mode = _widget.visible_child == _sidebar ? LeafletMode.SIDEBAR : LeafletMode.CONTENT; if (_folded && _visible_mode != mode) { _visible_mode = mode; notify_property ("visible-mode"); } }); } ~Leaflet () { _content.unparent (); _widget.widget.unparent (); } public bool folded { get { return _folded; } } public Gtk.Widget content { get { return _content; } set { _content = value; if (_folded) { _widget.set_child (_content_box, value); } else { _widget.set_child (_content_box, null); _content.set_parent (this); } queue_allocate (); } } public Gtk.Widget sidebar { get { return _sidebar; } set { _sidebar = value; _widget.set_child (_sidebar_box, value); queue_allocate (); } } public int visible_mode { get { return _visible_mode; } set { _visible_mode = value; update_visible_child (); } } public void pop () { visible_mode = LeafletMode.SIDEBAR; } public void push () { visible_mode = LeafletMode.CONTENT; } public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { var minimum1 = 0, minimum2 = 0; var natural1 = 0, natural2 = 0; _content.measure (orientation, for_size, out minimum1, out natural1, out minimum_baseline, out natural_baseline); _sidebar.measure (orientation, for_size, out minimum2, out natural2, out minimum_baseline, out natural_baseline); if (orientation == Gtk.Orientation.HORIZONTAL) { minimum = int.max (int.min (minimum1, minimum2), _content_min_width); } else { minimum = int.max (minimum1, minimum2); } natural = int.max (natural1, natural2); } public override void size_allocate (int width, int height, int baseline) { var first = _view_width == 0 && width > 0; if (first) { run_idle_once (() => { (_content as SizeWatcher)?.first_allocated (); (_sidebar as SizeWatcher)?.first_allocated (); }); } _view_width = width; _view_height = height; var stack = _widget.widget; var folded = width < _content_min_width * 2; if (_folded != folded || first) { _folded = folded; if (folded && !_content.is_ancestor (_content_box)) { _content.unparent (); _widget.set_child (_content_box, _content); } else if (!folded && _content.is_ancestor (_content_box)) { _widget.set_child (_content_box, null); _content.insert_after (this, _widget.widget); } var animate = _widget.animate_transitions; _widget.animate_transitions = false; update_visible_child (); _widget.animate_transitions = animate; notify_property ("folded"); } var allocation = Gtk.Allocation (); allocation.x = 0; allocation.y = 0; allocation.width = width; allocation.height = height; if (folded) { (_content as SizeWatcher)?.size_to_change (width, height); (_sidebar as SizeWatcher)?.size_to_change (width, height); _content.allocate_size (allocation, baseline); _sidebar.allocate_size (allocation, baseline); stack.allocate_size (allocation, baseline); } else { var rtl = get_direction () == Gtk.TextDirection.RTL; var content_width = (int) (width * _content_fraction).clamp (_content_min_width, _content_max_width); var side_width = width - content_width; // put sidebar at start allocation.x = rtl ? content_width : 0; allocation.width = side_width; (_sidebar as SizeWatcher)?.size_to_change (side_width, height); _sidebar.allocate_size (allocation, baseline); stack.allocate_size (allocation, baseline); // put content at end allocation.x = rtl ? 0 : side_width; allocation.width = content_width; (_content as SizeWatcher)?.size_to_change (content_width, height); _content.allocate_size (allocation, baseline); } } public override void snapshot (Gtk.Snapshot snapshot) { if (!_folded) { var size = _sidebar.get_width (); var rtl = get_direction () == Gtk.TextDirection.RTL; var rect = Graphene.Rect (); rect.init (rtl ? _view_width - size : size, 0, 0.5f, _view_height); var color = Gdk.RGBA (); color.red = color.green = color.blue = color.alpha = 0; #if GTK_4_10 var color2 = get_color (); #else var color2 = get_style_context ().get_color (); #endif color2.alpha = 0.25f; Gsk.ColorStop[] stops = { { 0, color }, { 0.5f, color2 }, { 1, color } }; snapshot.append_linear_gradient (rect, rect.get_top_left (), rect.get_bottom_right (), stops); } base.snapshot (snapshot); } private void update_visible_child () { _widget.visible_child = _folded && _visible_mode == LeafletMode.CONTENT ? _content : _sidebar; } } #if ADW_1_4 public class Stack : Object { private bool _retain_last_popped = false; private Adw.NavigationPage? _last_page = null; private Adw.NavigationView _widget = new Adw.NavigationView (); public Stack (bool retain_last_popped = false) { _retain_last_popped = retain_last_popped; _widget = new Adw.NavigationView (); _widget.get_next_page.connect (() => { return (_widget.visible_page != _last_page && _last_page?.get_child () != null) ? _last_page : null; }); _widget.pushed.connect(() => { notify_property ("visible-child"); }); _widget.popped.connect((page) => { notify_property ("visible-child"); if (_retain_last_popped) _last_page = page; }); } public bool animate_transitions { get { return _widget.animate_transitions; } set { _widget.animate_transitions = value; } } public Gtk.Widget parent { get { return _widget.parent; } } public Gtk.Widget visible_child { get { return _widget.visible_page.child; } set { if (_widget.visible_page.child != value) { if (_last_page?.child == value) { _widget.push ((!)_last_page); } else { var page = find_page (value); if (page != null) _widget.pop_to_page ((!)page); } } } } public Gtk.Widget widget { get { return _widget; } } public Gtk.Widget add (Gtk.Widget child, string? tag = null) { var page = new Adw.NavigationPage (child, tag ?? ""); page.set_tag (tag); if (_retain_last_popped) { _last_page = page; _widget.add (page); } else { _widget.push (page); } return page; } public void pop () { _widget.pop (); } public Gtk.Widget? get_child_by_name (string name) { return _widget.find_page (name)?.child; } public GenericArray get_children () { var pages = _widget.navigation_stack; var count = pages.get_n_items (); var children = new GenericArray (count); for (var i = 0; i < count; i++) { var page = (Adw.NavigationPage) pages.get_item (i); children.add (page.child); } return children; } public void get_visible_names (GenericArray names) { var pages = _widget.navigation_stack; var count = pages.get_n_items (); var visible_page = _widget.visible_page; for (var i = 0; i < count; i++) { var page = (Adw.NavigationPage) pages.get_item (i); names.add (page.tag); if (page == visible_page) break; } } public void set_child (Gtk.Widget page, Gtk.Widget? child) { var p = page as Adw.NavigationPage; p?.set_child (child); } private Adw.NavigationPage? find_page (Gtk.Widget child) { var pages = _widget.navigation_stack; var count = pages.get_n_items (); for (var i = 0; i < count; i++) { var page = (Adw.NavigationPage) pages.get_item (i); if (page.child == child) return page; } return null; } } #else public class Stack : Object { private Gtk.Stack _widget = new Gtk.Stack (); public Stack (bool retain_last_popped = false) { _widget = new Gtk.Stack (); animate_transitions = true; } public bool animate_transitions { get { return _widget.transition_type != Gtk.StackTransitionType.NONE; } set { _widget.transition_type = value ? Gtk.StackTransitionType.SLIDE_LEFT_RIGHT : Gtk.StackTransitionType.NONE; } } public Gtk.Widget parent { get { return _widget.parent; } } public Gtk.Widget visible_child { get { return ((Adw.Bin) _widget.visible_child).child; } set { _widget.visible_child = value.parent; } } public Gtk.Widget widget { get { return _widget; } } public Gtk.Widget add (Gtk.Widget child, string? tag = null) { var bin = new Adw.Bin (); bin.child = child; _widget.add_named (bin, tag); _widget.visible_child = bin; notify_property ("visible-child"); return bin; } public void pop () { var child = _widget.get_visible_child (); var previous = child?.get_prev_sibling (); if (child != null && previous != null) { _widget.visible_child = (!)previous; notify_property ("visible-child"); run_timeout_once (_widget.transition_duration, () => { _widget.remove ((!)child); }); } } public Gtk.Widget? get_child_by_name (string name) { return (_widget.get_child_by_name (name) as Adw.Bin)?.child; } public GenericArray get_children () { var pages = _widget.pages; var count = pages.get_n_items (); var children = new GenericArray (count); for (var i = 0; i < count; i++) { var page = (Gtk.StackPage) pages.get_item (i); children.add (((Adw.Bin) page.child).child); } return children; } public void get_visible_names (GenericArray names) { var pages = _widget.pages; var count = pages.get_n_items (); var child = _widget.visible_child; for (var i = 0; i < count; i++) { var page = (Gtk.StackPage) pages.get_item (i); names.add (page.name); if (page.child == child) break; } } public void set_child (Gtk.Widget page, Gtk.Widget? child) { (page as Adw.Bin)?.set_child (child); } } #endif } ================================================ FILE: src/ui/mini-bar.vala ================================================ namespace G4 { public class MiniBar : Adw.ActionRow { private Gtk.Image _cover = new Gtk.Image (); private StableLabel _title = new StableLabel (); private Gtk.Label _time = new Gtk.Label ("0:00"); private Gtk.Button _prev = new Gtk.Button (); private Gtk.Button _play = new Gtk.Button (); private Gtk.Button _next = new Gtk.Button (); private int _duration = 0; private int _position = 0; private Adw.Animation? _fade_animation = null; private CrossFadePaintable _paintable = new CrossFadePaintable (); construct { halign = Gtk.Align.FILL; hexpand = true; height_request = 60; var controller = new Gtk.GestureClick (); controller.released.connect (this.activate); add_controller (controller); activatable_widget = this; var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 2); vbox.halign = Gtk.Align.START; vbox.hexpand = true; vbox.valign = Gtk.Align.CENTER; vbox.append (_title); vbox.append (_time); add_prefix (vbox); add_prefix (_cover); _cover.valign = Gtk.Align.CENTER; _cover.margin_start = 2; _cover.margin_end = 6; _cover.pixel_size = 40; _cover.paintable = new RoundPaintable (_paintable); _paintable.queue_draw.connect (_cover.queue_draw); _title.halign = Gtk.Align.START; _title.marquee = true; _title.add_css_class ("title-leading"); _time.halign = Gtk.Align.START; _time.add_css_class ("dim-label"); _time.add_css_class ("numeric"); var font_size = _time.get_pango_context ().get_font_description ().get_size () / Pango.SCALE; if (font_size >= 13) _time.add_css_class ("title-secondly"); var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4); hbox.valign = Gtk.Align.CENTER; add_suffix (hbox); _prev.valign = Gtk.Align.CENTER; _prev.action_name = ACTION_APP + ACTION_PREV; _prev.icon_name = "media-skip-backward-symbolic"; _prev.tooltip_text = _("Play Previous"); _prev.add_css_class ("circular"); _prev.add_css_class ("flat"); hbox.append (_prev); _play.valign = Gtk.Align.CENTER; _play.action_name = ACTION_APP + ACTION_PLAY_PAUSE; _play.icon_name = "media-playback-start-symbolic"; _play.tooltip_text = _("Play/Pause"); _play.add_css_class ("circular"); _play.add_css_class ("flat"); hbox.append (_play); _next.valign = Gtk.Align.CENTER; _next.action_name = ACTION_APP + ACTION_NEXT; _next.icon_name = "media-skip-forward-symbolic"; _next.tooltip_text = _("Play Next"); _next.add_css_class ("circular"); _next.add_css_class ("flat"); hbox.append (_next); var app = (Application) GLib.Application.get_default (); var player = app.player; player.duration_changed.connect (on_duration_changed); player.position_updated.connect (on_position_changed); player.state_changed.connect (on_state_changed); } public Gdk.Paintable? cover { get { return _paintable.paintable; } set { _paintable.paintable = value; var target = new Adw.CallbackAnimationTarget ((value) => _paintable.fade = value); _fade_animation?.pause (); _fade_animation = new Adw.TimedAnimation (_cover, 1 - _paintable.fade, 0, 800, target); ((!)_fade_animation).done.connect (() => { _paintable.previous = null; _fade_animation = null; }); _fade_animation?.play (); } } public new string title { set { _title.label = value; } } public void size_to_change (int panel_width) { _prev.visible = panel_width >= 360; } public override void snapshot (Gtk.Snapshot snapshot) { var rect = Graphene.Rect (); rect.init (0, 0, get_width (), get_height ()); draw_outset_shadow (snapshot, rect); base.snapshot (snapshot); } private void on_duration_changed (Gst.ClockTime duration) { var value = GstPlayer.to_second (duration); if (_duration != (int) value) { _duration = (int) value; update_time_label (); } } private void on_position_changed (Gst.ClockTime position) { var value = GstPlayer.to_second (position); if (_position != (int) value) { _position = (int) value; update_time_label (); } } private void on_state_changed (Gst.State state) { var playing = state == Gst.State.PLAYING; _play.icon_name = playing ? "media-playback-pause-symbolic" : "media-playback-start-symbolic"; } private void update_time_label () { if (_duration > 0) _time.label = format_time (_position) + "/" + format_time (_duration); else _time.label = ""; } } } ================================================ FILE: src/ui/music-list.vala ================================================ namespace G4 { public enum Result { CANCEL = -1, FAILED = 0, OK = 1 } public class MusicList : Gtk.Box { protected Application _app; private HashTable _binding_items = new HashTable (direct_hash, direct_equal); private bool _compact_list = false; private Music? _current_node = null; private ListStore _data_store = new ListStore (typeof (Music)); private bool _editable = false; private Gtk.FilterListModel _filter_model = new Gtk.FilterListModel (null, null); private bool _grid_mode = false; private Gtk.GridView _grid_view = new Gtk.GridView (null, null); private int _image_size = Thumbnailer.ICON_SIZE; private Type _item_type = typeof (Music); protected bool _modified = false; private Music? _music_node = null; private Gtk.ScrolledWindow _scroll_view = new Gtk.ScrolledWindow (); private bool _selectable = false; private bool _single_click_activate = true; private Gtk.MultiSelection _selection; private Thumbnailer _thmbnailer; private uint _columns = 0; private Graphene.Size _cell_size = Graphene.Size (); private Graphene.Size _item_size = Graphene.Size (); private int _scrolling_item = -1; public signal void item_activated (uint position, Object? obj); public signal void item_binded (Gtk.ListItem item); public signal void item_created (Gtk.ListItem item); public signal void item_unbinded (Gtk.ListItem item); public MusicList (Application app, Type item_type = typeof (Music), Music? node = null, bool editable = false, bool selectable = true) { orientation = Gtk.Orientation.VERTICAL; hexpand = true; _app = app; _editable = editable; _filter_model.model = _data_store; _item_type = item_type; _music_node = node; _selectable = selectable; _thmbnailer = app.thumbnailer; _grid_view.max_columns = 5; _grid_view.margin_start = 6; _grid_view.margin_end = 6; _grid_view.model = _selection = new Gtk.MultiSelection (_filter_model); _grid_view.activate.connect ((position) => item_activated (position, _filter_model.get_item (position))); _grid_view.add_css_class ("navigation-sidebar"); _selection.selection_changed.connect (on_selection_changed); create_factory (); update_grid_view (); _scroll_view.child = _grid_view; _scroll_view.hscrollbar_policy = Gtk.PolicyType.NEVER; _scroll_view.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; _scroll_view.propagate_natural_height = true; _scroll_view.vexpand = true; append (_scroll_view); if (editable || item_type == typeof (Playlist)) { create_drop_target (_grid_view); } } public bool compact_list { get { return _compact_list; } set { if (_compact_list != value) { _compact_list = value; create_factory (); } } } public Music? current_node { set { var cur = get_binding_widget (_current_node); if (cur != null) ((!)cur).playing.visible = false; _current_node = value; var widget = get_binding_widget (value); if (widget != null) ((!)widget).playing.visible = true; } } public ListStore data_store { get { return _data_store; } set { _data_store = value; _filter_model.model = value; } } public int dropping_item { get { return _dropping_item; } set { if (_dropping_item != value) { _dropping_item = value; queue_draw (); } } } public Gtk.FilterListModel filter_model { get { return _filter_model; } } public bool grid_mode { get { return _grid_mode; } set { _image_size = value ? Thumbnailer.GRID_SIZE : Thumbnailer.ICON_SIZE; if (_grid_mode != value) { _grid_mode = value; create_factory (); } } } public Type item_type { get { return _item_type; } } public bool modified { get { return _modified; } set { _modified = value; } } protected bool _has_add_to_queque = true; protected bool _prompt_to_save = true; private GenericArray _action_buttons = new GenericArray (4); private Gtk.Widget? _header_bar_hided = null; private Gtk.Revealer? _header_revealer = null; private Gtk.Label? _header_title = null; private bool _multi_selection = false; public bool multi_selection { get { return _multi_selection; } set { if (value && _header_revealer == null) { var child = get_first_child (); _header_bar_hided = child != _scroll_view ? child : null; var header = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); setup_selection_header_bar (header); var revealer = new Gtk.Revealer (); revealer.child = header; revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_DOWN; revealer.insert_after (this, _header_bar_hided); _header_revealer = revealer; } _header_bar_hided?.set_visible (!value); _header_revealer?.set_reveal_child (value); if (_multi_selection != value) { _multi_selection = value; update_grid_view (); if (value) on_selection_changed (0, 0); else _selection.unselect_all (); } } } public Music? music_node { get { return _music_node; } } public bool playable { get { return _item_type == typeof (Music); } } public bool single_click_activate { get { return _single_click_activate; } set { if (_single_click_activate != value) { _single_click_activate = value; update_grid_view (); } } } public uint visible_count { get { return _filter_model.get_n_items (); } } public void activate_item (int item) { _grid_view.activate (item); } public void button_command (string name) { var playlist = create_playlist_for_selection (); switch (name) { case Button.ADDTO: _app.show_add_playlist_dialog.begin (playlist, (obj, res) => _app.show_add_playlist_dialog.end (res)); break; case Button.INSERT: _app.insert_after_current (playlist); break; case Button.QUEUE: _app.insert_to_queue (playlist); break; case Button.REMOVE: modified |= remove_items_from_store (_data_store, playlist.items) != 0; on_selection_changed (0, 0); break; } } public void create_factory () { var factory = new Gtk.SignalListItemFactory (); factory.setup.connect (on_create_item); factory.bind.connect (on_bind_item); factory.unbind.connect (on_unbind_item); _grid_view.factory = factory; } public Playlist get_as_playlist () { var pls = _music_node as Playlist; var playlist = new Playlist (pls?.title ?? "", pls?.list_uri ?? ""); var items = playlist.items; var count = _filter_model.get_n_items (); for (var i = 0; i < count; i++) { var music = (Music) _filter_model.get_item (i); items.add (music); } return playlist; } public virtual async Result save_if_modified (bool prompt = true) { if (_modified && _music_node is Playlist) { var playlist = get_as_playlist (); var ret = !prompt || yield show_alert_dialog (_("Playlist is modified, save it?"), root as Gtk.Window); if (ret) { ret = yield _app.add_playlist_to_file_async (playlist, false); if (ret) modified = false; return ret ? Result.OK : Result.FAILED; } update_store (); modified = false; } return _modified ? Result.CANCEL : Result.OK; } private Adw.Animation? _scroll_animation = null; public void scroll_to_item (int index, bool smoothly = true) { var adj = _scroll_view.vadjustment; var list_height = _grid_view.get_height (); var scroll_done = false; _columns = get_grid_view_item_size (_grid_view, ref _item_size, ref _cell_size); if (smoothly && _columns > 0 && _cell_size.height > 0 && adj.upper - adj.lower > list_height) { var from = adj.value; var row = index / _columns; var max_to = double.max ((row + 1) * _cell_size.height - list_height, 0); var min_to = double.max (row * _cell_size.height, 0); var scroll_to = from < max_to ? max_to : (from > min_to ? min_to : from); if ((scroll_to - from).abs () < list_height) { // Scroll smoothly var target = new Adw.CallbackAnimationTarget (adj.set_value); _scroll_animation?.pause (); _scroll_animation = new Adw.TimedAnimation (_scroll_view, adj.value, scroll_to, 500, target); _scroll_animation?.play (); scroll_done = true; } } if (!scroll_done) { scroll_to_directly (index); } // Hack: sometime show only first item if no child drawed, so scroll it when first draw an item _scrolling_item = index; } public void scroll_to_directly (uint index) { #if GTK_4_12 _grid_view.scroll_to (index, Gtk.ListScrollFlags.NONE, null); #else _grid_view.activate_action_variant ("list.scroll-to-item", new Variant.uint32 (index)); #endif } public override void snapshot (Gtk.Snapshot snapshot) { if (_header_revealer?.reveal_child ?? false) { var child = (!)_header_revealer; var rect = Graphene.Rect (); child.compute_bounds (this, out rect); draw_outset_shadow (snapshot, rect); } var rc_grid = Graphene.Rect (); if (_editable && _columns > 0 && _dropping_item >= 0 && _grid_view.compute_bounds (this, out rc_grid)) { var row = _dropping_item / _columns; var col = _dropping_item - _columns * row; var rc_cell = Graphene.Rect (); rc_cell.init (_cell_size.width * col, _cell_size.height * row, _cell_size.width, _cell_size.height); rc_cell.offset (rc_grid.origin.x, rc_grid.origin.y); rc_cell.origin.y -= (float) (_scroll_view.vadjustment.value) - (_cell_size.height - _item_size.height); rc_cell.size.height = 1; #if ADW_1_6 var color = Adw.StyleManager.get_default ().accent_color.to_rgba (); #elif GTK_4_10 var color = get_color (); #else var color = get_style_context ().get_color (); #endif snapshot.append_color (color, rc_cell); } base.snapshot (snapshot); } private Gtk.Label? _empty_label = null; public void set_empty_text (string? text) { if (_data_store.get_n_items () > 0 && _empty_label != null) { remove ((!)_empty_label); _empty_label = null; } else if (_data_store.get_n_items () == 0 && _empty_label == null) { _empty_label = new Gtk.Label (text); ((!)_empty_label).margin_top = 8; prepend ((!)_empty_label); } } public int set_to_current_item (bool scroll = true) { var item = find_item_in_model (_filter_model, _current_node); if (item != -1 && scroll) scroll_to_item (item); return item; } public void update_item_cover (Music music, Gdk.Paintable paintable) { var item = _binding_items[music]; if (item is Gtk.ListItem) { item_unbinded (item); item_binded (item); var widget = item.child as MusicWidget; if (widget != null) ((!)widget).paintable = paintable; } } public uint update_store () { if (_music_node is Playlist) { ((Playlist) _music_node).overwrite_to (_data_store); } else if (_music_node is Album) { ((Album) _music_node).overwrite_to (_data_store); } else if (_music_node is Artist) { ((Artist) _music_node).overwrite_store (_data_store); } return _data_store.get_n_items (); } private Menu? on_create_music_menu (Music? node) { int position = -1; if (_multi_selection && (position = find_item_in_model (_filter_model, node)) != -1) { _selection.select_item (position, false); } if (_selection.get_selection ().get_size () > 1) { var action = ACTION_WIN + ACTION_BUTTON; var menu = new Menu (); menu.append_item (create_menu_item_for_button (Button.INSERT, _("Play at _Next"), action)); if (_has_add_to_queque) menu.append_item (create_menu_item_for_button (Button.QUEUE, _("Add to _Queue"), action)); menu.append_item (create_menu_item_for_button (Button.ADDTO, _("Add to _Playlist…"), action)); menu.append_item (create_menu_item_for_button (Button.REMOVE, _("_Remove"), action)); return menu; } else if (node is Album) { return create_menu_for_album ((Album) node); } else if (node is Artist) { return create_menu_for_artist ((Artist) node); } else if (node is Music) { var music = (Music) node; unowned var uri = music.uri; var menu = create_menu_for_music (music, _app.thumbnailer.find (music) is Gdk.Texture); if (music != _app.current_music) { /* Translators: Play this music at next position of current playing music */ menu.insert_item (0, create_menu_item_for_uri (uri, _("Play at _Next"), ACTION_APP + ACTION_PLAY_AT_NEXT)); if (_has_add_to_queque) menu.insert_item (1, create_menu_item_for_uri (uri, _("Add to _Queue"), ACTION_APP + ACTION_ADD_TO_QUEUE)); } if (_editable) { var section = new Menu (); section.append_item (create_menu_item_for_uri (uri, _("_Remove"), ACTION_WIN + ACTION_REMOVE)); section.append_item (create_menu_item_for_uri (uri, _("_Move to Trash"), ACTION_APP + ACTION_TRASH_FILE)); menu.append_section (null, section); } return menu; } return null; } private void on_create_item (Object obj) { var child = _grid_mode ? (MusicWidget) new MusicCell () : (MusicWidget) new MusicEntry (_compact_list); var item = (Gtk.ListItem) obj; item.child = child; item.selectable = _multi_selection || !_single_click_activate; item_created (item); if (_selectable) { create_drag_source (child.image, item); child.create_music_menu.connect (on_create_music_menu); make_right_clickable (child, child.show_popover_menu); make_long_pressable (child, (widget, x, y) => multi_selection = true); } } private void on_bind_item (Object obj) { var item = (Gtk.ListItem) obj; var child = (MusicWidget) item.child; var music = (Music) item.item; child.playing.visible = music == _current_node; item_binded (item); _binding_items[music] = item; var paintable = _thmbnailer.find (music, _image_size); if (paintable != null) { child.paintable = paintable; } else { child.first_draw_handler = child.cover.first_draw.connect (() => { child.disconnect_first_draw (); _thmbnailer.load_async.begin (music, _image_size, (obj, res) => { var paintable2 = _thmbnailer.load_async.end (res); if (music == (Music) item.item) { child.paintable = paintable2; } }); if (_scrolling_item != -1) { scroll_to_directly (_scrolling_item); _scrolling_item = -1; } }); } } private void on_unbind_item (Object obj) { var item = (Gtk.ListItem) obj; var child = (MusicWidget) item.child; child.paintable = null; child.disconnect_first_draw (); item_unbinded (item); _binding_items.remove ((Music) item.item); } private void on_drag_begin (Gtk.DragSource source, Gdk.Drag drag, Gtk.Widget widget, Graphene.Point point) { var size = _selection.get_selection ().get_size (); var title = size > 1 ? size.to_string () : (string) null; source.set_icon (create_widget_paintable (widget, ref point, title), (int) point.x, (int) point.y); } private Gdk.ContentProvider? on_drag_prepare (Music? music) { var position = find_item_in_model (_filter_model, music); if (position != -1) _selection.select_item (position, false); var playlist = create_playlist_for_selection (); return create_content_provider (playlist); } private int _dropping_item = -1; private bool on_drop_done (Value value, double x, double y) { if (_editable) { uint position = uint.min (_dropping_item, visible_count); if (value.holds (typeof (Playlist))) { var dst_obj = _filter_model.get_item (position); if (dst_obj == null || !_data_store.find ((!)dst_obj, out position)) position = _data_store.get_n_items (); var playlist = (Playlist) value.get_object (); modified |= merge_items_to_store (_data_store, playlist.items, ref position); } else { var files = get_dropped_files (value); _app.open_files_async.begin (files, position, false, (obj, res) => modified |= _app.open_files_async.end (res)); } } dropping_item = -1; return true; } private uint _activate_handle = 0; private Gdk.DragAction on_drop_motion (double x, double y) { _columns = get_grid_view_item_size (_grid_view, ref _item_size, ref _cell_size); var col = (int) (x / _cell_size.width); var row = (int) ((y + _scroll_view.vadjustment.value) / _cell_size.height); var index = (int) _columns * row + col; if (_dropping_item != index && _filter_model.get_item (index) is Playlist) { if (_activate_handle != 0) Source.remove (_activate_handle); _activate_handle = run_timeout_once (1000, () => { _activate_handle = 0; if (_dropping_item == index) _grid_view.activate (index); }); } dropping_item = int.min (index, (int) visible_count); return Gdk.DragAction.LINK; } private MusicWidget? get_binding_widget (Music? music) { var item = music != null ? _binding_items[(!)music] : (Gtk.ListItem?) null; return item?.child as MusicWidget; } private static void create_drag_source (Gtk.Widget widget, Gtk.ListItem item) { // Hack: don't use `this` directly, because it will not be destroyed when detach??? var point = Graphene.Point (); var source = new Gtk.DragSource (); source.actions = Gdk.DragAction.LINK; source.drag_begin.connect ((drag) => { var list = find_ancestry_with_type (widget, typeof (MusicList)); (list as MusicList)?.on_drag_begin (source, drag, widget, point); }); source.prepare.connect ((x, y) => { point.init ((float) x, (float) y); var list = find_ancestry_with_type (widget, typeof (MusicList)); return (list as MusicList)?.on_drag_prepare (item.item as Music); }); widget.add_controller (source); } private void create_drop_target (Gtk.Widget widget) { var target = new Gtk.DropTarget (Type.INVALID, Gdk.DragAction.COPY | Gdk.DragAction.LINK); target.set_gtypes ({ typeof (Playlist), typeof (Gdk.FileList) }); target.accept.connect ((drop) => drop.formats.contain_gtype (typeof (Playlist)) || drop.formats.contain_gtype (typeof (Gdk.FileList))); target.motion.connect (on_drop_motion); target.leave.connect (() => run_timeout_once (100, () => dropping_item = -1)); #if GTK_4_10 target.drop.connect (on_drop_done); #else target.on_drop.connect (on_drop_done); #endif widget.add_controller (target); } private Playlist create_playlist_for_selection () { var count = _filter_model.get_n_items (); var musics = new GenericArray (count); for (var i = 0; i < count; i++) { if (_selection.is_selected (i)) musics.add ((Music) _filter_model.get_item (i)); } return to_playlist (musics.data, _music_node?.title); } private uint _remove_selection_handle = 0; private void on_selection_changed (uint position, uint n_items) { var bits = _selection.get_selection (); var selected = bits.get_size (); if (selected > 1) multi_selection = true; _header_title?.set_label (@"$selected/$visible_count"); var enabled = selected > 0; foreach (var button in _action_buttons) button.sensitive = enabled; if (_remove_selection_handle != 0) Source.remove (_remove_selection_handle); _remove_selection_handle = run_timeout_once (3000, () => { _remove_selection_handle = 0; if (!_multi_selection && bits.get_size () == 1) _selection.unselect_item (bits.get_minimum ()); }); } private void setup_selection_header_bar (Gtk.Box header) { header.hexpand = true; header.add_css_class ("flat"); header.add_css_class ("toolbar"); var back_btn = new Gtk.Button.from_icon_name ("go-previous-symbolic"); back_btn.clicked.connect (() => { if (_modified && _prompt_to_save) { save_if_modified.begin (true, (obj, res) => { var ret = save_if_modified.end (res); if (ret != Result.FAILED) { modified = false; multi_selection = false; } }); } else { multi_selection = false; } }); back_btn.tooltip_text = _("Back"); header.append (back_btn); var all_btn = new Gtk.Button.from_icon_name ("edit-select-all-symbolic"); all_btn.tooltip_text = _("Select All"); all_btn.clicked.connect (() => { if (_selection.get_selection ().get_size () == visible_count) _selection.unselect_all (); else _selection.select_all (); }); header.append (all_btn); var title = new Gtk.Label (null); title.add_css_class ("dim-label"); title.halign = Gtk.Align.CENTER; title.hexpand = true; header.append (title); _header_title = title; var insert_btn = new Gtk.Button.from_icon_name ("format-indent-more-symbolic"); insert_btn.tooltip_text = _("Play at Next"); insert_btn.clicked.connect (() => button_command (Button.INSERT)); insert_btn.name = Button.INSERT; _action_buttons.add (insert_btn); if (_has_add_to_queque) { var queue_btn = new Gtk.Button.from_icon_name ("document-send-symbolic"); queue_btn.tooltip_text = _("Add to Queue"); queue_btn.clicked.connect (() => button_command (Button.QUEUE)); queue_btn.name = Button.QUEUE; _action_buttons.add (queue_btn); } var addto_btn = new Gtk.Button.from_icon_name ("document-new-symbolic"); addto_btn.tooltip_text = _("Add to Playlist"); addto_btn.clicked.connect (() => button_command (Button.ADDTO)); addto_btn.name = Button.ADDTO; _action_buttons.add (addto_btn); if (_editable) { var remove_btn = new Gtk.Button.from_icon_name ("user-trash-symbolic"); remove_btn.tooltip_text = _("Remove"); remove_btn.clicked.connect (() => button_command (Button.REMOVE)); remove_btn.name = Button.REMOVE; _action_buttons.add (remove_btn); } _action_buttons.foreach (header.append); } private void update_grid_view () { var enable = _multi_selection || !_single_click_activate; _grid_view.enable_rubberband = enable; _grid_view.single_click_activate = !enable; _binding_items.foreach ((music, item) => item.selectable = enable); } } public class MainMusicList : MusicList { public MainMusicList (Application app) { base (app, typeof (Music), null, true); _has_add_to_queque = false; _prompt_to_save = false; } public override async Result save_if_modified (bool prompt = true) { if (_modified) { var ret = yield _app.save_queue_to_file (); if (ret) modified = false; return ret ? Result.OK : Result.FAILED; } return Result.OK; } } public Gdk.ContentProvider create_content_provider (Playlist playlist) { var val = Value (typeof (Playlist)); val.set_object (playlist); #if GTK_4_8 var files = new GenericArray (playlist.length); playlist.items.foreach ((music) => files.add (File.new_for_uri (music.uri))); var val2 = Value (typeof (Gdk.FileList)); val2.set_boxed (new Gdk.FileList.from_array (files.data)); return new Gdk.ContentProvider.union ({ new Gdk.ContentProvider.for_value (val), new Gdk.ContentProvider.for_value (val2), }); #else return new Gdk.ContentProvider.for_value (val); #endif } namespace Button { public const string ADDTO = "add"; public const string INSERT = "insert"; public const string QUEUE = "queue"; public const string REMOVE = "remove"; } public MenuItem create_menu_item_for_button (string button_name, string label, string action) { var item = new MenuItem (label, null); item.set_action_and_target_value (action, new Variant.string (button_name)); return item; } public Gtk.Widget? find_ancestry_with_type (Gtk.Widget widget, Type type) { var parent = widget.get_parent (); if (parent?.get_type ()?.is_a (type) ?? false) return parent as MusicList; else if (parent != null) return find_ancestry_with_type ((!)parent, type); return null; } public struct CountedRect { public int count; public int index; public Graphene.Rect rect; public CountedRect (int c = 0, int i = 0, Graphene.Rect? rc = null) { count = c; index = i; if (rc != null) rect = (!)rc; else rect.init (0, 0, 0, 0); } } public uint get_grid_view_item_size (Gtk.GridView grid_view, ref Graphene.Size inner_size, ref Graphene.Size outer_size) { var arr = new GenericArray (); var w_map = new HashTable (null, null); var x_set = new GenericSet (null, null); for (var c = grid_view.get_first_child (); c != null; c = c?.get_next_sibling ()) { var child = (!)c; var rect = Graphene.Rect (); if (child.is_drawable () && child.compute_bounds (grid_view, out rect) && rect.origin.x >= 0 && rect.origin.y >= 0 && rect.size.width > 0 && rect.size.height > 0) { var width = (int) rect.size.width; var count = 1, index = arr.length; var cr0 = w_map[width]; if (cr0 != null) { count = ((!)cr0).count + 1; index = ((!)cr0).index; } var cr = CountedRect (count, index, rect); w_map[width] = cr; x_set.add ((int) rect.origin.x); arr.add (rect); } } var columns = x_set.length.clamp (grid_view.min_columns, grid_view.max_columns); var best_rect = CountedRect (); best_rect.index = -1; w_map.foreach ((w, rect) => { var cr = (!)rect; if (best_rect.count < cr.count) { best_rect.count = cr.count; if (best_rect.index == -1) best_rect.index = cr.index; best_rect.rect = cr.rect; } }); inner_size.init_from_size (best_rect.rect.size); outer_size.init_from_size (inner_size); var index = best_rect.index; if (index >= 0 && index + 1 < arr.length) { outer_size.width = columns > 1 ? ((!)arr[index + 1]).origin.x - ((!)arr[index]).origin.x : grid_view.get_width () / int.max (1, (int) columns); if (index + columns < arr.length) outer_size.height = ((!)arr[index + columns]).origin.y - ((!)arr[index]).origin.y; } return columns; } } ================================================ FILE: src/ui/music-widgets.vala ================================================ namespace G4 { namespace PageName { public const string ALBUM = "album"; public const string ARTIST = "artist"; public const string PLAYING = "playing"; public const string PLAYLIST = "playlist"; } public class MusicWidget : Gtk.Box { protected Gtk.Image _image = new Gtk.Image (); protected StableLabel _title = new StableLabel (); protected StableLabel _subtitle = new StableLabel (); protected RoundPaintable _paintable = new RoundPaintable (); protected Gtk.Image _playing = new Gtk.Image (); public ulong first_draw_handler = 0; public Music? music = null; public signal Menu? create_music_menu (Music? node); public MusicWidget () { _playing.halign = Gtk.Align.END; _playing.valign = Gtk.Align.CENTER; _playing.icon_name = "media-playback-start-symbolic"; _playing.margin_end = 4; _playing.pixel_size = 10; _playing.visible = false; _playing.add_css_class ("dim-label"); } public RoundPaintable cover { get { return _paintable; } } public Gtk.Image image { get { return _image; } } public Gdk.Paintable? paintable { set { _paintable.paintable = value; } } public Gtk.Image playing { get { return _playing; } } public string title { set { _title.label = value; } } public string subtitle { set { _subtitle.label = value; _subtitle.visible = value.length > 0; } } public void disconnect_first_draw () { if (first_draw_handler != 0) { _paintable.disconnect (first_draw_handler); first_draw_handler = 0; } } public void show_popover_menu (Gtk.Widget widget, double x, double y) { var menu = create_music_menu (music); if (menu != null) { var popover = create_popover_menu ((!)menu, x, y); popover.set_parent (widget); popover.popup (); } } } public class MusicCell : MusicWidget { public MusicCell () { orientation = Gtk.Orientation.VERTICAL; margin_top = 10; margin_bottom = 10; width_request = 200; var overlay = new Gtk.Overlay (); overlay.margin_bottom = 8; overlay.child = _image; overlay.add_overlay (_playing); append (overlay); _image.halign = Gtk.Align.CENTER; _image.pixel_size = 160; _image.paintable = _paintable; _paintable.queue_draw.connect (_image.queue_draw); _title.halign = Gtk.Align.CENTER; _title.ellipsize = EllipsizeMode.MIDDLE; _title.margin_start = 2; _title.margin_end = 2; _title.add_css_class ("title-leading"); append (_title); _subtitle.halign = Gtk.Align.CENTER; _subtitle.ellipsize = EllipsizeMode.MIDDLE; _subtitle.margin_start = 2; _subtitle.margin_end = 2; _subtitle.visible = false; _subtitle.add_css_class ("dim-label"); var font_size = _subtitle.get_pango_context ().get_font_description ().get_size () / Pango.SCALE; if (font_size >= 13) _subtitle.add_css_class ("title-secondly"); append (_subtitle); } } public class MusicEntry : MusicWidget { public MusicEntry (bool compact = true) { width_request = 324; var cover_margin = compact ? 3 : 4; var cover_size = compact ? 36 : 48; _image.margin_top = cover_margin; _image.margin_bottom = cover_margin; _image.margin_start = 4; _image.pixel_size = cover_size; _image.paintable = _paintable; _paintable.queue_draw.connect (_image.queue_draw); append (_image); var spacing = compact ? 2 : 6; var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, spacing); vbox.hexpand = true; vbox.valign = Gtk.Align.CENTER; vbox.margin_start = 12; vbox.margin_end = 4; vbox.append (_title); vbox.append (_subtitle); append (vbox); _title.halign = Gtk.Align.START; _title.ellipsize = EllipsizeMode.END; _title.add_css_class ("title-leading"); _subtitle.halign = Gtk.Align.START; _subtitle.ellipsize = EllipsizeMode.END; _subtitle.add_css_class ("dim-label"); var font_size = _subtitle.get_pango_context ().get_font_description ().get_size () / Pango.SCALE; if (font_size >= 13) _subtitle.add_css_class ("title-secondly"); append (_playing); } public void set_titles (Music music, uint sort) { this.music = music; switch (sort) { case SortMode.ALBUM: _title.label = music.album; _subtitle.label = (0 < music.track < int.MAX) ? @"$(music.track). $(music.title)" : music.title; break; case SortMode.ARTIST: _title.label = music.artist; _subtitle.label = music.title; break; case SortMode.ARTIST_ALBUM: _title.label = @"$(music.artist): $(music.album)"; _subtitle.label = (0 < music.track < int.MAX) ? @"$(music.track). $(music.title)" : music.title; break; case SortMode.RECENT: var date = new DateTime.from_unix_local (music.modified_time); _title.label = music.title; _subtitle.label = date.format ("%x %H:%M"); break; default: _title.label = music.title; _subtitle.label = music.artist; break; } } } public string[] build_action_target_for_album (Album album) { unowned var album_artist = album.album_artist; unowned var album_key = album.album_key; var is_playlist = album is Playlist; if (is_playlist) return { PageName.PLAYLIST, album_key }; else if (album_artist.length > 0) return { PageName.ARTIST, album_artist, album_key }; else return { PageName.ALBUM, album_key }; } public const string LIBRARY_SCHEME = "library://"; public string build_library_uri (Artist? artist, Album? album) { var album_key = album?.album_key ?? ""; var arr = (artist != null) ? new string[] { PageName.ARTIST, ((!)artist).artist_name, album_key } : (album is Playlist) ? new string[] { PageName.PLAYLIST, album_key } : (album != null) ? new string[] { PageName.ALBUM, album_key } : new string[] { PageName.PLAYING }; return build_library_uri_from_sa (arr); } public string build_library_uri_from_sa (string[] arr) { var sb = new StringBuilder (LIBRARY_SCHEME); if (arr.length > 0) { sb.append (arr[0]); for (var i = 1; i < arr.length; i++) { sb.append_c ('/'); sb.append (Uri.escape_string (arr[i])); } } return sb.str; } public bool parse_library_uri (string uri_str, out string? artist, out string? album, out string? playlist, out string? host = null) { host = null; artist = null; album = null; playlist = null; if (uri_str.has_prefix (LIBRARY_SCHEME)) { var path = uri_str.substring (LIBRARY_SCHEME.length); var arr = path.split ("/"); if (arr.length > 1) { var key = Uri.unescape_string (arr[1]) ?? ""; switch (arr[0]) { case PageName.ALBUM: album = key; break; case PageName.ARTIST: artist = key; if (arr.length > 2) album = Uri.unescape_string (arr[2]); break; case PageName.PLAYLIST: playlist = key; break; } } host = arr[0]; return true; } return false; } public MenuItem create_menu_item_for_strv (string[] strv, string label, string action) { var item = new MenuItem (label, null); item.set_action_and_target_value (action, new Variant.strv (strv)); return item; } public MenuItem create_menu_item_for_uri (string uri, string label, string action) { var item = new MenuItem (label, null); item.set_action_and_target_value (action, new Variant.string (uri)); return item; } public Menu create_menu_for_album (Album album) { var uri = build_library_uri (null, album); var menu = new Menu (); menu.append_item (create_menu_item_for_uri (uri, _("Play"), ACTION_APP + ACTION_PLAY)); menu.append_item (create_menu_item_for_uri (uri, _("_Random Play"), ACTION_APP + ACTION_RANDOM_PLAY)); var section = new Menu (); section.append_item (create_menu_item_for_uri (uri, _("Play at _Next"), ACTION_APP + ACTION_PLAY_AT_NEXT)); section.append_item (create_menu_item_for_uri (uri, _("Add to _Queue"), ACTION_APP + ACTION_ADD_TO_QUEUE)); section.append_item (create_menu_item_for_uri (uri, _("Add to _Playlist…"), ACTION_APP + ACTION_ADD_TO_PLAYLIST)); menu.append_section (null, section); if (album is Playlist) { unowned var list_uri = ((Playlist) album).list_uri; if (list_uri.length > 0) { var section2 = new Menu (); section2.append_item (create_menu_item_for_uri (list_uri, _("Show List _File"), ACTION_APP + ACTION_SHOW_FILE)); section2.append_item (create_menu_item_for_uri (list_uri, _("_Move to Trash"), ACTION_APP + ACTION_TRASH_FILE)); menu.append_section (null, section2); } } return menu; } public Menu create_menu_for_artist (Artist artist) { var uri = build_library_uri (artist, null); var menu = new Menu (); menu.append_item (create_menu_item_for_uri (uri, _("Play"), ACTION_APP + ACTION_PLAY)); menu.append_item (create_menu_item_for_uri (uri, _("_Random Play"), ACTION_APP + ACTION_RANDOM_PLAY)); var section = new Menu (); section.append_item (create_menu_item_for_uri (uri, _("Play at _Next"), ACTION_APP + ACTION_PLAY_AT_NEXT)); section.append_item (create_menu_item_for_uri (uri, _("Add to _Queue"), ACTION_APP + ACTION_ADD_TO_QUEUE)); section.append_item (create_menu_item_for_uri (uri, _("Add to _Playlist…"), ACTION_APP + ACTION_ADD_TO_PLAYLIST)); menu.append_section (null, section); return menu; } public Menu create_menu_for_music (Music music, bool has_cover) { var section = new Menu (); section.append_item (create_menu_item_for_strv ({"title", music.title}, _("Search Title"), ACTION_WIN + ACTION_SEARCH)); section.append_item (create_menu_item_for_strv ({"album", music.album}, _("Search Album"), ACTION_WIN + ACTION_SEARCH)); section.append_item (create_menu_item_for_strv ({"artist", music.artist}, _("Search Artist"), ACTION_WIN + ACTION_SEARCH)); unowned var uri = music.uri; var section2 = new Menu (); if (music.cover_uri != null) section2.append_item (create_menu_item_for_uri ((!)music.cover_uri, _("Show Cover _File"), ACTION_APP + ACTION_SHOW_FILE)); else if (has_cover) section2.append_item (create_menu_item_for_uri (uri, _("_Export Cover"), ACTION_APP + ACTION_EXPORT_COVER)); section2.append_item (create_menu_item_for_uri (uri, _("Show _Tags…"), ACTION_APP + ACTION_SHOW_TAGS)); section2.append_item (create_menu_item_for_uri (uri, _("Show Music _File"), ACTION_APP + ACTION_SHOW_FILE)); var menu = new Menu (); menu.append_item (create_menu_item_for_uri (uri, _("Add to _Playlist…"), ACTION_APP + ACTION_ADD_TO_PLAYLIST)); menu.append_section (null, section); menu.append_section (null, section2); return menu; } public Gtk.PopoverMenu create_popover_menu (Menu menu, double x, double y) { var rect = Gdk.Rectangle (); rect.x = (int) x; rect.y = (int) y; rect.width = rect.height = 0; var popover = new Gtk.PopoverMenu.from_model (menu); popover.autohide = true; popover.halign = Gtk.Align.START; popover.has_arrow = false; popover.pointing_to = rect; return popover; } public delegate void Pressed (Gtk.Widget widget, double x, double y); public Gtk.GestureLongPress make_long_pressable (Gtk.Widget widget, Pressed pressed) { var gesture = new Gtk.GestureLongPress (); gesture.pressed.connect ((x, y) => pressed (widget, x, y)); widget.add_controller (gesture); return gesture; } public Gtk.GestureClick make_right_clickable (Gtk.Widget widget, Pressed pressed) { var gesture = new Gtk.GestureClick (); gesture.button = Gdk.BUTTON_SECONDARY; gesture.pressed.connect ((n, x, y) => pressed (widget, x, y)); widget.add_controller (gesture); return gesture; } public void remove_controllers (Gtk.Widget widget) { var controllers = widget.observe_controllers (); for (var i = (int) controllers.get_n_items () - 1; i >= 0; i--) { var controller = (Gtk.EventController) controllers.get_item (i); widget.remove_controller (controller); } } } ================================================ FILE: src/ui/narrow-bar.vala ================================================ namespace G4 { public class NarrowBar : Gtk.Widget { private Gtk.Widget? _child = null; private int _minimum_width = 0; private bool _reveal = true; public Gtk.Widget? child { get { return _child; } set { _child?.unparent (); _child = value; _child?.set_parent (this); queue_allocate (); } } public bool reveal { get { return _reveal; } set { _reveal = value; queue_allocate (); } } public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (_child != null) { ((!)_child).measure (orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); } else { minimum = natural = minimum_baseline = natural_baseline = 0; } if (orientation == Gtk.Orientation.HORIZONTAL) { _minimum_width = minimum; minimum = _minimum_width / 2; } } public override void size_allocate (int width, int height, int baseline) { var wide = width >= _minimum_width; if (_reveal != wide) { _reveal = wide; notify_property ("reveal"); if (wide && _child?.parent == null) { _child?.set_parent (this); } else if (!wide && _child?.parent != null) { _child?.unparent (); } } if (_child != null) { var allocation = Gtk.Allocation (); allocation.x = 0; allocation.y = 0; allocation.width = width; allocation.height = height; ((!)_child).allocate_size (allocation, baseline); } } } } ================================================ FILE: src/ui/paintables.vala ================================================ namespace G4 { public class BasePaintable : Object, Gdk.Paintable { private bool _first_draw = false; private Gdk.Paintable? _paintable = null; public signal void first_draw (); public signal void queue_draw (); public BasePaintable (Gdk.Paintable? paintable = null) { _paintable = paintable; } public Gdk.Paintable? paintable { get { return _paintable; } set { if (_paintable != value) { on_change (_paintable, value); _paintable = value; queue_draw (); } } } public virtual Gdk.Paintable get_current_image () { return _paintable?.get_current_image () ?? this; } public virtual Gdk.PaintableFlags get_flags () { return _paintable?.get_flags () ?? 0; } public virtual double get_intrinsic_aspect_ratio () { return _paintable?.get_intrinsic_aspect_ratio () ?? 1; } public virtual int get_intrinsic_width () { return _paintable?.get_intrinsic_width () ?? 1; } public virtual int get_intrinsic_height () { return _paintable?.get_intrinsic_height () ?? 1; } public void snapshot (Gdk.Snapshot shot, double width, double height) { if (_first_draw) { _first_draw = false; first_draw (); } on_snapshot ((!)(shot as Gtk.Snapshot), width, height); } protected virtual void on_change (Gdk.Paintable? previous, Gdk.Paintable? paintable) { _first_draw = previous != paintable; } protected virtual void on_snapshot (Gtk.Snapshot snapshot, double width, double height) { _paintable?.snapshot (snapshot, width, height); } } public class RoundPaintable : BasePaintable { private Gsk.RoundedRect _bounds = Gsk.RoundedRect (); private double _ratio = 0; public RoundPaintable (Gdk.Paintable? paintable = null, double ratio = 0.1) { base (paintable); _ratio = ratio; } public double ratio { get { return _ratio; } set { if (_ratio != value) { _ratio = value; queue_draw (); } } } public bool contains (Graphene.Point point) { return _bounds.contains_point (point); } protected override void on_snapshot (Gtk.Snapshot snapshot, double width, double height) { var rect = Graphene.Rect (); var size = (float) double.min (width, height); var circle = _ratio >= 0.5; if (circle) // Force clip to circle rect.init ((float) (width - size) * 0.5f, (float) (height - size) * 0.5f, size, size); else rect.init (0, 0, (float) width, (float) height); var radius = (float) (_ratio * size); _bounds.init_from_rect (rect, radius); var saved = false; if (radius > 0) { if (circle && width != height) { float scale = (float) double.max (width, height) / size; saved = true; snapshot.save (); compute_matrix (snapshot, width, height, 0, scale); } snapshot.push_rounded_clip (_bounds); } base.on_snapshot (snapshot, width, height); if (radius > 0) { snapshot.pop (); } if (saved) { snapshot.restore (); } } } public class CrossFadePaintable : BasePaintable { private Gdk.Paintable? _previous = null; private double _fade = 0; public CrossFadePaintable (Gdk.Paintable? paintable = null) { base (paintable); } public double fade { get { return _fade; } set { if (_fade != value) { _fade = value; queue_draw (); } } } public Gdk.Paintable? previous { get { return _previous; } set { if (_previous != value) { _previous = value; queue_draw (); } } } protected override void on_change (Gdk.Paintable? previous, Gdk.Paintable? paintable) { _previous = previous; base.on_change (previous, paintable); } protected override void on_snapshot (Gtk.Snapshot snapshot, double width, double height) { if (_fade > 0 && _previous != null) { var width2 = width; var height2 = height; var prev = (!)_previous; var ratio2 = prev.get_intrinsic_aspect_ratio (); var point = Graphene.Point (); var different = ratio2 != get_intrinsic_aspect_ratio (); if (different) { var max_side = double.max (width, height); if (ratio2 < 1) { height2 = max_side; width2 = height2 * ratio2; } else { width2 = max_side; height2 = width2 / ratio2; } point.x = (float) (width - width2) * 0.5f; point.y = (float) (height - height2) * 0.5f; snapshot.translate (point); } snapshot.push_opacity (_fade); prev.snapshot (snapshot, width2, height2); snapshot.pop (); if (different) { point.x = - point.x; point.y = - point.y; snapshot.translate (point); } } if (_fade > 0) { snapshot.push_opacity (1 - _fade); } base.on_snapshot (snapshot, width, height); if (_fade > 0) { snapshot.pop (); } // print ("fade: %g\n", _fade); } } public class MatrixPaintable : BasePaintable { private double _rotation = 0; private double _scale = 1; public MatrixPaintable (Gdk.Paintable? paintable = null) { base (paintable); } public double rotation { get { return _rotation; } set { if (_rotation != value) { _rotation = value; queue_draw (); } } } public double scale { get { return _scale; } set { if (_scale != value) { _scale = value; queue_draw (); } } } protected override void on_snapshot (Gtk.Snapshot snapshot, double width, double height) { var saved = _rotation != 0 || _scale != 1; if (saved) { snapshot.save (); compute_matrix (snapshot, width, height, _rotation, _scale); } base.on_snapshot (snapshot, width, height); if (saved) { snapshot.restore (); } } } public void compute_matrix (Gtk.Snapshot snapshot, double width, double height, double rotation = 0, double scale = 1) { var point = Graphene.Point (); point.init ((float) (width * 0.5), (float) (height * 0.5)); snapshot.translate (point); if (rotation != 0) snapshot.rotate ((float) rotation); if (scale != 1) snapshot.scale ((float) scale, (float) scale); point.init (- point.x, - point.y); snapshot.translate (point); } public const uint32[] BACKGROUND_COLORS = { 0xff83b6ecu, 0xff337fdcu, // blue 0xff7ad9f1u, 0xff0f9ac8u, // cyan 0xff8de6b1u, 0xff29ae74u, // green 0xffb5e98au, 0xff6ab85bu, // lime 0xfff8e359u, 0xffd29d09u, // yellow 0xffffcb62u, 0xffd68400u, // gold 0xffffa95au, 0xffed5b00u, // orange 0xfff78773u, 0xffe62d42u, // raspberry 0xffe973abu, 0xffe33b6au, // magenta 0xffcb78d4u, 0xff9945b5u, // purple 0xff9e91e8u, 0xff7a59cau, // violet 0xffe3cf9cu, 0xffb08952u, // beige 0xffbe916du, 0xff785336u, // brown 0xffc0bfbcu, 0xff6e6d71u, // gray }; public Gdk.RGBA color_from_uint (uint color) { var c = Gdk.RGBA (); c.alpha = ((color >> 24) & 0xff) / 255f; c.red = ((color >> 16) & 0xff) / 255f; c.green = ((color >> 8) & 0xff) / 255f; c.blue = (color & 0xff) / 255f; return c; } public Gdk.Paintable? create_blur_paintable (Gtk.Widget widget, Gdk.Paintable paintable, int size, double blur = 80, double opacity = 0.25) { var snapshot = new Gtk.Snapshot (); snapshot.push_opacity (opacity); snapshot.push_blur (blur); paintable.snapshot (snapshot, size, size); snapshot.pop (); snapshot.pop (); var rect = Graphene.Rect (); rect.init (0, 0, size, size); Gdk.Paintable? result = null; var node = snapshot.free_to_node (); if (node is Gsk.RenderNode) { result = widget.get_native ()?.get_renderer ()?.render_texture ((!)node, rect); } return result ?? snapshot.free_to_paintable (rect.size); } public Pango.Layout create_center_text_layout (Pango.Context context, string family, int width, int height, double font_size) { var font = new Pango.FontDescription (); font.set_absolute_size (font_size * Pango.SCALE); font.set_family (family); font.set_weight (Pango.Weight.BOLD); var layout = new Pango.Layout (context); layout.set_alignment (Pango.Alignment.CENTER); layout.set_font_description (font); layout.set_width (width * Pango.SCALE); layout.set_height (height * Pango.SCALE); layout.set_single_paragraph_mode (true); return layout; } public Gdk.Paintable? create_text_paintable (Pango.Context context, string text, int width, int height, uint color_index = 0x7fffffff) { var snapshot = new Gtk.Snapshot (); var rect = Graphene.Rect (); rect.init (0, 0, width, height); var c = Gdk.RGBA (); c.alpha = 1f; if (color_index < BACKGROUND_COLORS.length / 2) { c.red = c.green = c.blue = 0.9f; var c1 = color_from_uint (BACKGROUND_COLORS[color_index * 2]); var c2 = color_from_uint (BACKGROUND_COLORS[color_index * 2 + 1]); Gsk.ColorStop[] stops = { { 0, c1 }, { 0.5f, c2 }, { 1, c1 } }; snapshot.append_linear_gradient (rect, rect.get_top_left (), rect.get_bottom_right (), stops); } else { c.red = c.green = c.blue = 0.5f; } var ink_rect = Pango.Rectangle (); var logic_rect = Pango.Rectangle (); var layout = create_center_text_layout (context, "Serif", width, height, height * 0.4); layout.set_text (text, text.length); layout.get_pixel_extents (out ink_rect, out logic_rect); var pt = Graphene.Point (); pt.x = 0; pt.y = - ink_rect.y + (height - ink_rect.height) * 0.5f; snapshot.translate (pt); snapshot.append_layout (layout, c); pt.y = - pt.y; snapshot.translate (pt); return snapshot.free_to_paintable (rect.size); } public unowned string TEXT_SVG_FORMAT = """ %s """; public string create_text_svg (Pango.Context context, string text, uint color_index = 0x7fffffff) { var rect = Graphene.Rect (); var width = 128, height = 128; rect.init (0, 0, width, height); uint c1 = 0, c2 = 0; if (color_index < BACKGROUND_COLORS.length / 2) { c1 = BACKGROUND_COLORS[color_index * 2] & 0x00ffffffu; c2 = BACKGROUND_COLORS[color_index * 2 + 1] & 0x00ffffffu; } var ink_rect = Pango.Rectangle (); var logic_rect = Pango.Rectangle (); var layout = create_center_text_layout (context, "Serif", width, height, height * 0.4); layout.set_text (text, text.length); layout.get_pixel_extents (out ink_rect, out logic_rect); var x = (width - logic_rect.width) * 0.5f; var y = - ink_rect.y + (height + logic_rect.height) * 0.5f; return TEXT_SVG_FORMAT.printf (c1, c2, c1, x, y, text); } public Gdk.Paintable? create_widget_paintable (Gtk.Widget widget, ref Graphene.Point point, string? title = null, int max_size = 64) { float width = widget.get_width (); float height = widget.get_height (); var scale = (width > max_size || height > max_size) ? max_size / float.max (width, height) : 1; width *= scale; height *= scale; point.x *= scale; point.y *= scale; var paintable = new Gtk.WidgetPaintable (widget); var snapshot = new Gtk.Snapshot (); if (title != null) { var text = (!)title; var ink_rect = Pango.Rectangle (); var logic_rect = Pango.Rectangle (); var layout = create_center_text_layout (widget.get_pango_context (), "Sans", (int) width, (int) height, height * 0.2); layout.set_text (text, text.length); layout.get_pixel_extents (out ink_rect, out logic_rect); var pt = Graphene.Point (); pt.x = 0; pt.y = logic_rect.height * 0.5f; point.y += pt.y; snapshot.translate (pt); snapshot.scale (scale, scale); paintable.snapshot (snapshot, widget.get_width (), widget.get_height ()); snapshot.scale (1 / scale, 1 / scale); pt.y = -pt.y; snapshot.translate (pt); pt.x = width - ink_rect.x - logic_rect.width * 0.5f; pt.y = 2; snapshot.translate (pt); var rect = Graphene.Rect (); rect.init (0, 0, int.max (logic_rect.width, logic_rect.height), logic_rect.height); rect.offset ((width - rect.size.width) * 0.5f, 0); rect.inset (-2, -2); var bounds = Gsk.RoundedRect (); var radius = rect.size.height * 0.5f; bounds.init_from_rect (rect, radius); snapshot.push_rounded_clip (bounds); #if ADW_1_6 var color = Adw.StyleManager.get_default ().accent_color.to_rgba (); #else var color = Gdk.RGBA (); color.red = color.green = color.blue = 0.5f; color.alpha = 1; #endif snapshot.append_color (color, rect); color.red = color.green = color.blue = 1; pt.x = 0; pt.y = - ink_rect.y + (logic_rect.height - ink_rect.height) * 0.5f; snapshot.translate (pt); snapshot.append_layout (layout, color); snapshot.pop (); color.alpha = 0.2f; color.red = color.green = color.blue = 0; snapshot.append_outset_shadow (bounds, color, 1, 1, 1, 5); } else { snapshot.scale (scale, scale); paintable.snapshot (snapshot, widget.get_width (), widget.get_height ()); snapshot.scale (1 / scale, 1 / scale); } return snapshot.free_to_paintable (null); } public void draw_outset_shadow (Gtk.Snapshot snapshot, Graphene.Rect rect, float radius = 5) { var bounds = rect; bounds.inset (radius, radius); var outline = Gsk.RoundedRect (); outline.init_from_rect (bounds, radius); var style = Adw.StyleManager.get_default (); if (style.high_contrast) { var color = Gdk.RGBA (); color.alpha = 0.5f; color.red = color.green = color.blue = style.dark ? 1 : 0; float[] border_width = { 1, 1, 1, 1 }; Gdk.RGBA[] border_color = { color, color, color, color }; snapshot.append_border (outline, border_width, border_color); } else { var color = Gdk.RGBA (); color.alpha = 0.2f; color.red = color.green = color.blue = 0; snapshot.append_outset_shadow (outline, color, 1, 1, 1, float.max (radius - 1, 0)); } } } ================================================ FILE: src/ui/peak-bar.vala ================================================ namespace G4 { public class PeakBar : Gtk.Widget { private unichar[] _chars = { '=', 0 }; private int _char_count = 1; private int[] _char_widths = { 10 }; private int _char_height = 0; private Pango.Layout _layout; private StringBuilder _sbuilder = new StringBuilder (); private double _value = 0; construct { _layout = create_pango_layout (null); _layout.set_alignment (get_direction () == Gtk.TextDirection.RTL ? Pango.Alignment.RIGHT : Pango.Alignment.LEFT); } public Pango.Alignment align { get { return _layout.get_alignment (); } set { _layout.set_alignment (value); queue_draw (); } } public string characters { get { return ((string32) _chars).to_string () ?? ""; } set { var count = value.char_count (); _chars = new unichar[count + 1]; _char_count = 0; _char_height = 0; var next = 0; unichar c = 0; while (value.get_next_char (ref next, out c)) { if (!c.ismark ()) { Pango.Rectangle ink_rect, logic_rect; var text = c.to_string (); _layout.set_text (text, text.length); _layout.get_pixel_extents (out ink_rect, out logic_rect); _chars[_char_count] = c; _char_widths[_char_count] = logic_rect.width; if (_char_height < logic_rect.height) _char_height = logic_rect.height; _char_count++; } } _chars[_char_count] = 0; _char_widths[_char_count] = 0; queue_resize (); queue_draw (); } } public double peak { get { return _value; } set { _value = value; queue_draw (); } } public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (orientation == Gtk.Orientation.VERTICAL) { minimum = natural = minimum_baseline = natural_baseline = _char_height; } else { minimum = natural = minimum_baseline = natural_baseline = for_size; } } public override void snapshot (Gtk.Snapshot snapshot) { var width = get_width (); var height = get_height (); var center = _layout.get_alignment () == Pango.Alignment.CENTER; var value_width = _value * width; _layout.set_width (-1); _layout.set_height (height * Pango.SCALE); var char_count = 0; var char_width = 0; _sbuilder.truncate (); if (_char_count > 0) { var last = _char_count - 1; _sbuilder.append_unichar (_chars[0]); char_count++; char_width += _char_widths[0]; if (_char_count >= 2) { char_count++; char_width += _char_widths[last]; } var char1 = _chars[_char_count >= 3 ? 1 : 0]; var cx1 = _char_widths[_char_count >= 3 ? 1 : 0]; var char2 = _chars[_char_count >= 3 ? _char_count - 2 : _char_count - 1]; var cx2 = _char_widths[_char_count >= 3 ? _char_count - 2 : _char_count - 1]; if (char1 == char2) { var count = (int) ((value_width - char_width) / cx1 + 0.5); if (center && (count + _char_count) % 2 == 0) count--; for (var i = 0; i < count; i++) { _sbuilder.append_unichar (char1); char_count++; char_width += cx1; } } else { var count = (int) ((value_width - char_width) / (cx1 + cx2) + 0.5); if (center && (count + _char_count) % 2 == 0) count--; for (var i = 0; i < count; i++) { _sbuilder.append_unichar (char1); char_count++; char_width += cx1; } for (var j = 0; j < count; j++) { _sbuilder.append_unichar (char2); char_count++; char_width += cx2; } } if (_char_count >= 2) { _sbuilder.append_unichar (_chars[last]); } } #if GTK_4_10 var color = get_color (); #else var color = get_style_context ().get_color (); #endif var opacity = char_width > value_width ? value_width / char_width : 1; Pango.Rectangle ink_rect, logic_rect; _layout.set_text (_sbuilder.str, (int) _sbuilder.len); _layout.get_pixel_extents (out ink_rect, out logic_rect); var pt = Graphene.Point (); pt.x = center ? - ink_rect.x + (width - ink_rect.width) * 0.5f : 0; pt.y = - ink_rect.y + (height - ink_rect.height) * 0.5f; snapshot.translate (pt); if (opacity < 1) snapshot.push_opacity (opacity); snapshot.append_layout (_layout, color); if (opacity < 1) snapshot.pop (); pt.x = - pt.x; pt.y = - pt.y; snapshot.translate (pt); } } } ================================================ FILE: src/ui/play-bar.vala ================================================ namespace G4 { public class PlayBar : Gtk.Box { private Gtk.Scale _seek = new Gtk.Scale (Gtk.Orientation.HORIZONTAL, null); private PeakBar _peak = new PeakBar (); private Gtk.Label _positive = new Gtk.Label ("0:00"); private Gtk.Label _negative = new Gtk.Label ("0:00"); private Gtk.ToggleButton _repeat = new Gtk.ToggleButton (); private Gtk.Button _prev = new Gtk.Button (); private Gtk.Button _play = new Gtk.Button (); private Gtk.Button _next = new Gtk.Button (); private VolumeButton _volume = new VolumeButton (); private int _duration = 0; private int _position = 0; private bool _remain_progress = false; private bool _seeking = false; public signal void position_seeked (double position); construct { orientation = Gtk.Orientation.VERTICAL; var app = (Application) GLib.Application.get_default (); var player = app.player; _seek.set_range (0, _duration); _seek.halign = Gtk.Align.FILL; append (_seek); setup_seek_bar (player); var times = new Gtk.CenterBox (); times.baseline_position = Gtk.BaselinePosition.CENTER; times.halign = Gtk.Align.FILL; times.set_start_widget (_positive); times.set_end_widget (_negative); var overlay = new Gtk.Overlay (); overlay.child = times; overlay.add_overlay (_peak); append (overlay); _peak.align = Pango.Alignment.CENTER; _peak.halign = Gtk.Align.CENTER; _peak.width_request = 168; _peak.add_css_class ("dim-label"); _positive.halign = Gtk.Align.START; _positive.margin_start = 12; _positive.add_css_class ("dim-label"); _positive.add_css_class ("numeric"); _negative.halign = Gtk.Align.END; _negative.margin_end = 12; _negative.add_css_class ("dim-label"); _negative.add_css_class ("numeric"); make_widget_clickable (_negative).pressed.connect (() => remain_progress = !remain_progress); var buttons = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 16); buttons.halign = Gtk.Align.CENTER; buttons.margin_top = 16; buttons.append (_repeat); buttons.append (_prev); buttons.append (_play); buttons.append (_next); buttons.append (_volume); append (buttons); _repeat.icon_name = "media-playlist-repeat-symbolic"; _repeat.valign = Gtk.Align.CENTER; /* Translators: single loop the current music */ _repeat.tooltip_text = _("Single Loop"); _repeat.add_css_class ("flat"); _repeat.toggled.connect (() => { _repeat.icon_name = _repeat.active ? "media-playlist-repeat-song-symbolic" : "media-playlist-repeat-symbolic"; app.single_loop = ! app.single_loop; }); _prev.valign = Gtk.Align.CENTER; _prev.action_name = ACTION_APP + ACTION_PREV; _prev.icon_name = "media-skip-backward-symbolic"; _prev.tooltip_text = _("Play Previous"); _prev.add_css_class ("circular"); _play.valign = Gtk.Align.CENTER; _play.action_name = ACTION_APP + ACTION_PLAY_PAUSE; _play.icon_name = "media-playback-start-symbolic"; // media-playback-pause-symbolic _play.tooltip_text = _("Play/Pause"); _play.add_css_class ("circular"); _play.set_size_request (48, 48); _next.valign = Gtk.Align.CENTER; _next.action_name = ACTION_APP + ACTION_NEXT; _next.icon_name = "media-skip-forward-symbolic"; _next.tooltip_text = _("Play Next"); _next.add_css_class ("circular"); _volume.valign = Gtk.Align.CENTER; player.bind_property ("volume", _volume, "value", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); player.duration_changed.connect (on_duration_changed); player.position_updated.connect (on_position_changed); player.state_changed.connect (on_state_changed); var settings = app.settings; settings.bind ("show-peak", _peak, "visible", SettingsBindFlags.DEFAULT); settings.bind ("peak-characters", _peak, "characters", SettingsBindFlags.DEFAULT); settings.bind ("remain-progress", this, "remain-progress", SettingsBindFlags.DEFAULT); } public double peak { set { _peak.peak = value; } } public double position { get { return _seek.get_value (); } } public bool remain_progress { get { return _remain_progress; } set { _remain_progress = value; update_negative_label (); } } public void on_size_changed (int bar_width, int bar_spacing) { var text_width = int.max (_positive.get_width (), _negative.get_width ()); _peak.width_request = bar_width - (text_width + _positive.margin_start + _negative.margin_end) * 2; get_last_child ()?.set_margin_top (bar_spacing); } private void on_duration_changed (Gst.ClockTime duration) { var value = GstPlayer.to_second (duration); _duration = (int) (value + 0.5); _seek.set_range (0, _duration); update_negative_label (); } private void on_position_changed (Gst.ClockTime position) { if (!_seeking) { update_position (position); } } private void on_state_changed (Gst.State state) { var playing = state == Gst.State.PLAYING; _play.icon_name = playing ? "media-playback-pause-symbolic" : "media-playback-start-symbolic"; } private void setup_seek_bar (GstPlayer player) { _seek.change_value.connect ((type, value) => { if (_seeking) { position_seeked (value); update_position (GstPlayer.from_second (value)); return true; } return false; }); // Hack that grabs the click gesture controller as mouse released event doesn't work otherwise // Bug: https://gitlab.gnome.org/GNOME/gtk/-/issues/4939 Gtk.GestureClick? click_gesture = null; var controllers = _seek.observe_controllers (); for (var i = 0; i < controllers.get_n_items (); i++) { var controller = controllers.get_item (i); if (controller is Gtk.GestureClick) { click_gesture = (Gtk.GestureClick) controller; break; } } if (click_gesture == null) { click_gesture = new Gtk.GestureClick (); _seek.add_controller ((!)click_gesture); } var gesture = (!)click_gesture; gesture.set_button (0); gesture.pressed.connect(() => _seeking = true); gesture.released.connect(() => { _seeking = false; player.seek(GstPlayer.from_second (_seek.get_value ())); }); } private void update_negative_label () { if (_remain_progress) _negative.label = "-" + format_time (_duration - _position); else _negative.label = format_time (_duration); } private void update_position (Gst.ClockTime position) { var value = GstPlayer.to_second (position); if (_position != (int) value) { _position = (int) value; _positive.label = format_time (_position); if (_remain_progress) _negative.label = "-" + format_time (_duration - _position); } _seek.set_value (value); } } public static string format_time (int seconds) { var sb = new StringBuilder (); var hours = seconds / 3600; var minutes = seconds / 60; seconds -= minutes * 60; if (hours > 0) { minutes -= hours * 60; sb.printf ("%d:%02d:%02d", hours, minutes, seconds); } else { sb.printf ("%d:%02d", minutes, seconds); } return sb.str; } public static Gtk.GestureClick make_widget_clickable (Gtk.Widget label) { var controller = new Gtk.GestureClick (); controller.button = Gdk.BUTTON_PRIMARY; label.add_controller (controller); label.set_cursor_from_name ("pointer"); return controller; } } ================================================ FILE: src/ui/play-panel.vala ================================================ namespace G4 { [GtkTemplate (ui = "/com/github/neithern/g4music/gtk/play-panel.ui")] public class PlayPanel : Gtk.Box, SizeWatcher { [GtkChild] private unowned Gtk.MenuButton action_btn; [GtkChild] private unowned Gtk.Button back_btn; [GtkChild] private unowned Gtk.Label index_label; [GtkChild] private unowned Gtk.Box music_box; [GtkChild] private unowned Gtk.Image music_cover; [GtkChild] private unowned StableLabel music_album; [GtkChild] private unowned StableLabel music_artist; [GtkChild] private unowned StableLabel music_title; [GtkChild] private unowned Gtk.Label initial_label; private PlayBar _play_bar = new PlayBar (); private Application _app; private double _degrees_per_second = 360 / 20; // 20s per lap private CrossFadePaintable _crossfade_paintable = new CrossFadePaintable (); private MatrixPaintable _matrix_paintable = new MatrixPaintable (); private RoundPaintable _round_paintable = new RoundPaintable (); private bool _rotate_cover = true; private bool _show_peak = true; private bool _size_allocated = false; public signal void cover_changed (Music? music, CrossFadePaintable cover); public PlayPanel (Application app, Window win, Leaflet leaflet) { _app = app; _play_bar.halign = Gtk.Align.FILL; _play_bar.position_seeked.connect (on_position_seeked); music_box.append (_play_bar); leaflet.bind_property ("folded", back_btn, "visible", BindingFlags.SYNC_CREATE); action_btn.set_create_popup_func (() => action_btn.menu_model = create_music_action_menu ()); back_btn.clicked.connect (leaflet.pop); _matrix_paintable.paintable = _round_paintable; _crossfade_paintable.paintable = _matrix_paintable; _crossfade_paintable.queue_draw.connect (music_cover.queue_draw); music_cover.paintable = _crossfade_paintable; create_drag_source (); index_label.tooltip_text = _("Playing"); make_widget_clickable (index_label).released.connect (() => win.open_playing_page ()); initial_label.activate_link.connect (on_music_folder_clicked); music_album.tooltip_text = _("Search Album"); music_artist.tooltip_text = _("Search Artist"); music_title.tooltip_text = _("Search Title"); make_widget_clickable (music_album).released.connect ( () => win.start_search (music_album.label, SearchMode.ALBUM)); make_widget_clickable (music_artist).released.connect ( () => win.start_search (music_artist.label, SearchMode.ARTIST)); make_widget_clickable (music_title).released.connect ( () => win.start_search (music_title.label, SearchMode.TITLE)); make_right_clickable (music_box, show_popover_menu); app.index_changed.connect (on_index_changed); app.music_changed.connect (on_music_changed); app.music_cover_parsed.connect (on_music_cover_parsed); app.player.state_changed.connect (on_player_state_changed); var settings = app.settings; settings.bind ("rotate-cover", this, "rotate-cover", SettingsBindFlags.DEFAULT); settings.bind ("show-peak", this, "show-peak", SettingsBindFlags.DEFAULT); } public bool rotate_cover { get { return _rotate_cover; } set { _rotate_cover = value; _round_paintable.ratio = value ? 0.5 : 0.05; _matrix_paintable.rotation = value ? _play_bar.position * _degrees_per_second : 0; on_player_state_changed (_app.player.state); } } public bool show_peak { get { return _show_peak; } set { _show_peak = value; on_player_state_changed (_app.player.state); } } public void first_allocated () { // Delay update info after the window size allocated to avoid showing slowly _size_allocated = true; on_music_changed (_app.current_music); } public void size_to_change (int width, int height) { var max_size = int.max (width * 3 / 4, music_cover.pixel_size); var margin_horz = (width - max_size) / 2; var margin_cover = int.max (margin_horz, 32); music_cover.margin_start = margin_cover; music_cover.margin_end = margin_cover; var margin_bar = int.max (margin_horz / 2, 16); var spacing = (height - 540).clamp (8, 16); _play_bar.margin_start = margin_bar; _play_bar.margin_end = margin_bar; _play_bar.margin_top = spacing; _play_bar.margin_bottom = spacing * 2; _play_bar.on_size_changed (width - margin_bar * 2, spacing); } private void create_drag_source () { var point = Graphene.Point (); var source = new Gtk.DragSource (); source.actions = Gdk.DragAction.LINK; source.drag_begin.connect ((drag) => source.set_icon (create_widget_paintable (music_cover, ref point), (int) point.x, (int) point.y)); source.prepare.connect ((x, y) => { var pt = Graphene.Point (); pt.init ((float) x, (float) y); var width = music_cover.get_width (); var height = music_cover.get_height (); if (width > height) pt.x -= (width - height) * 0.5f; else if (height > width) pt.y -= (height - width) * 0.5f; var music = _app.current_music; if (music != null && _round_paintable.contains (pt)) { point.init ((float) x, (float) y); var playlist = to_playlist ({ (!)music }); return create_content_provider (playlist); } return null; }); music_cover.add_controller (source); } private Menu create_music_action_menu () { var music = _app.current_music ?? new Music.empty (); return create_menu_for_music (music, _app.current_cover != null); } private void on_index_changed (int index, uint size) { root.action_set_enabled (ACTION_APP + ACTION_PREV, index > 0); root.action_set_enabled (ACTION_APP + ACTION_NEXT, index < (int) size - 1); index_label.label = size > 0 ? @"$(index+1)/$(size)" : ""; } private void on_music_changed (Music? music) { if (!_size_allocated) return; music_album.label = music?.album ?? ""; music_artist.label = music?.artist ?? ""; music_title.label = music?.title ?? ""; var empty = _app.current_music == null && _app.current_list.get_n_items () == 0; initial_label.visible = empty; if (empty) { if (_app.loading || !_app.loader.library.empty) initial_label.label = ""; else update_initial_label (_app.music_folder); } var enabled = music != null; if (!enabled) { update_cover_paintables (music, _app.icon); } action_btn.sensitive = enabled; root.action_set_enabled (ACTION_APP + ACTION_PLAY_PAUSE, enabled); Window.get_default ()?.set_title (music?.get_artist_and_title () ?? _app.name); } private bool on_music_folder_clicked (string uri) { pick_music_folder (_app, root as Window, (dir) => update_initial_label (dir.get_uri ())); return true; } private async void on_music_cover_parsed (Music music, Gdk.Pixbuf? pixbuf, string? uri) { var paintable = pixbuf != null ? Gdk.Texture.for_pixbuf ((!)pixbuf) : _app.thumbnailer.create_music_text_paintable (music); update_cover_paintables (music, paintable); } private Adw.Animation? _scale_animation = null; private uint _tick_handler = 0; private int64 _tick_last_time = 0; private void on_player_state_changed (Gst.State state) { var playing = state == Gst.State.PLAYING; if (state >= Gst.State.PAUSED) { var target = new Adw.CallbackAnimationTarget ((value) => _matrix_paintable.scale = value); _scale_animation?.pause (); _scale_animation = new Adw.TimedAnimation (music_cover, _matrix_paintable.scale, _rotate_cover || playing ? 1 : 0.85, 500, target); _scale_animation?.play (); } var need_tick = _rotate_cover || _show_peak; if (need_tick && playing && _tick_handler == 0) { _tick_last_time = get_monotonic_time (); _tick_handler = add_tick_callback (on_tick_callback); } else if ((!need_tick || !playing) && _tick_handler != 0) { remove_tick_callback (_tick_handler); _tick_handler = 0; } } private void on_position_seeked (double pos) { if (_rotate_cover) _matrix_paintable.rotation = pos * _degrees_per_second; } private bool on_tick_callback (Gtk.Widget widget, Gdk.FrameClock clock) { if (_rotate_cover) { var now = get_monotonic_time (); var elapsed = (now - _tick_last_time) / 1e6; var angle = elapsed * _degrees_per_second; _matrix_paintable.rotation += angle; _tick_last_time = now; } if (_show_peak) { var peak = _app.player.peak; _play_bar.peak = peak; } return true; } private void show_popover_menu (Gtk.Widget widget, double x, double y) { if (_app.current_music != null) { var menu = create_music_action_menu (); var popover = create_popover_menu (menu, x, y); popover.set_parent (widget); popover.popup (); } } private void update_cover_paintables (Music? music, Gdk.Paintable? paintable) { _round_paintable = new RoundPaintable (paintable); _round_paintable.ratio = _rotate_cover ? 0.5 : 0.05; _round_paintable.queue_draw.connect (music_cover.queue_draw); _matrix_paintable = new MatrixPaintable (_round_paintable); _matrix_paintable.queue_draw.connect (music_cover.queue_draw); _crossfade_paintable.paintable = _matrix_paintable; cover_changed (music, _crossfade_paintable); } private void update_initial_label (string uri) { var dir_name = Uri.escape_string (get_display_name (uri)); var link = @"$dir_name"; initial_label.set_markup (_("Drag and drop music files here,\nor change music location: ") + link); } } } ================================================ FILE: src/ui/playlist-dialog.vala ================================================ namespace G4 { public class PlaylistDialog : Dialog { private Gtk.ToggleButton search_btn = new Gtk.ToggleButton (); private Gtk.SearchEntry search_entry = new Gtk.SearchEntry (); private Application _app; private SourceFunc? _callback = null; private MusicList? _list = null; private Playlist? _playlist = null; public PlaylistDialog (Application app) { _app = app; var content = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); this.child = content; var header = new Gtk.HeaderBar (); header.title_widget = new Gtk.Label (_("Add to Playlist")); header.add_css_class ("flat"); content.append (header); var new_btn = new Gtk.Button.from_icon_name ("folder-new-symbolic"); new_btn.tooltip_text = _("New Playlist"); new_btn.clicked.connect (() => close_with_result (new Playlist (""))); header.pack_start (new_btn); header.pack_end (search_btn); var search_bar = new Gtk.SearchBar (); search_bar.child = search_entry; search_bar.key_capture_widget = content; content.append (search_bar); search_btn.icon_name = "edit-find-symbolic"; search_btn.tooltip_text = _("Search"); search_btn.toggled.connect (on_search_btn_toggled); search_btn.bind_property ("active", search_bar, "search-mode-enabled", BindingFlags.BIDIRECTIONAL); search_entry.hexpand = true; search_entry.search_changed.connect (on_search_text_changed); var loading_paintable = app.thumbnailer.create_simple_text_paintable ("...", Thumbnailer.ICON_SIZE); var list = new MusicList (app, typeof (Playlist), null, false, false); list.hexpand = true; list.vexpand = true; list.margin_bottom = 2; list.item_activated.connect ((position, obj) => close_with_result (obj as Playlist)); list.item_created.connect ((item) => { var cell = (MusicWidget) item.child; cell.playing.icon_name = "document-open-recent-symbolic"; }); list.item_binded.connect ((item) => { var cell = (MusicWidget) item.child; var playlist = (Playlist) item.item; cell.music = playlist; cell.paintable = loading_paintable; cell.title = playlist.title; }); content.append (list); _list = list; app.music_library_changed.connect (on_music_library_changed); on_music_library_changed (true); } public async Playlist? choose (Gtk.Window? parent = null) { _callback = choose.callback; present (parent); yield; return _playlist; } public override void closed () { _app.music_library_changed.disconnect (on_music_library_changed); var list = _list; _list = null; run_idle_once (() => list?.unparent (), Priority.LOW); var callback = _callback; _callback = null; if (callback != null) Idle.add ((!)callback); } private void close_with_result (Playlist? playlist) { _playlist = playlist; if ((playlist?.list_uri?.length ?? 0) > 0) _app.settings.set_string ("recent-playlist", ((!)playlist).list_uri); close (); } private void on_music_library_changed (bool external) { if (external && _list != null) { var list = (!)_list; var library = _app.loader.library; var store = list.data_store; var text = _("No playlist found in %s").printf (get_display_name (_app.music_folder)); library.overwrite_playlists_to (store); list.set_empty_text (text); var recent_uri = _app.settings.get_string ("recent-playlist"); list.current_node = library.get_playlist ((!)recent_uri); list.set_to_current_item (true); } } private void on_search_btn_toggled () { if (search_btn.active) { search_entry.grab_focus (); } on_search_text_changed (); } private string _search_text = ""; private bool on_search_match (Object obj) { unowned var playlist = (Playlist) obj; return _search_text.match_string (playlist.title, true); } private void on_search_text_changed () { _search_text = search_entry.text; var model = _list?.filter_model; if (search_btn.active && model?.get_filter () == null) { model?.set_filter (new Gtk.CustomFilter (on_search_match)); } else if (!search_btn.active && model?.get_filter () != null) { model?.set_filter (null); } model?.get_filter ()?.changed (Gtk.FilterChange.DIFFERENT); } } } ================================================ FILE: src/ui/preferences.vala ================================================ namespace G4 { namespace BlurMode { public const uint NEVER = 0; public const uint ALWAYS = 1; public const uint ART_ONLY = 2; } [GtkTemplate (ui = "/com/github/neithern/g4music/gtk/preferences.ui")] public class PreferencesWindow : Adw.PreferencesWindow { [GtkChild] unowned Adw.ComboRow blur_row; [GtkChild] unowned Gtk.Switch compact_btn; [GtkChild] unowned Gtk.Switch grid_btn; [GtkChild] unowned Gtk.Switch single_btn; [GtkChild] unowned Gtk.Button music_dir_btn; [GtkChild] unowned Gtk.Switch monitor_btn; [GtkChild] unowned Gtk.Switch thumbnail_btn; [GtkChild] unowned Gtk.Switch playbkgnd_btn; [GtkChild] unowned Gtk.Switch rotate_btn; [GtkChild] unowned Gtk.Switch gapless_btn; [GtkChild] unowned Adw.ComboRow replaygain_row; [GtkChild] unowned Adw.ComboRow audiosink_row; [GtkChild] unowned Adw.ExpanderRow peak_row; [GtkChild] unowned Gtk.Entry peak_entry; private GenericArray _audio_sinks = new GenericArray (8); public PreferencesWindow (Application app) { var settings = app.settings; blur_row.model = new Gtk.StringList ({_("Never"), _("Always"), _("Art Only")}); settings.bind ("blur-mode", blur_row, "selected", SettingsBindFlags.DEFAULT); settings.bind ("compact-playlist", compact_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("grid-mode", grid_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("single-click-activate", single_btn, "active", SettingsBindFlags.DEFAULT); music_dir_btn.label = get_display_name (app.music_folder); music_dir_btn.clicked.connect (() => { pick_music_folder (app, this, (dir) => { music_dir_btn.label = get_display_name (app.music_folder); }); }); settings.bind ("monitor-changes", monitor_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("remote-thumbnail", thumbnail_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("play-background", playbkgnd_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("rotate-cover", rotate_btn, "active", SettingsBindFlags.DEFAULT); replaygain_row.model = new Gtk.StringList ({_("Never"), _("Track"), _("Album")}); settings.bind ("replay-gain", replaygain_row, "selected", SettingsBindFlags.DEFAULT); settings.bind ("gapless-playback", gapless_btn, "active", SettingsBindFlags.DEFAULT); settings.bind ("show-peak", peak_row, "enable_expansion", SettingsBindFlags.DEFAULT); settings.bind ("peak-characters", peak_entry, "text", SettingsBindFlags.DEFAULT); GstPlayer.get_audio_sinks (_audio_sinks); var sink_names = new string[_audio_sinks.length]; for (var i = 0; i < _audio_sinks.length; i++) sink_names[i] = get_audio_sink_name (_audio_sinks[i]); audiosink_row.model = new Gtk.StringList (sink_names); this.bind_property ("audio_sink", audiosink_row, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); } public uint audio_sink { get { var app = (Application) GLib.Application.get_default (); var sink_name = app.player.audio_sink; for (int i = 0; i < _audio_sinks.length; i++) { if (sink_name == _audio_sinks[i].name) return i; } return _audio_sinks.length > 0 ? 0 : -1; } set { if (value < _audio_sinks.length) { var app = (Application) GLib.Application.get_default (); app.player.audio_sink = _audio_sinks[value].name; } } } } public string get_audio_sink_name (Gst.ElementFactory factory) { var name = factory.get_metadata ("long-name") ?? factory.name; name = name.replace ("Audio sink", "") .replace ("Audio Sink", "") .replace ("sink", "") .replace ("(", "").replace (")", ""); return name.strip (); } public delegate void FolderPicked (File dir); public void pick_music_folder (Application app, Gtk.Window? parent, FolderPicked picked) { var music_dir = File.new_for_uri (app.music_folder); show_select_folder_dialog.begin (parent, music_dir, (obj, res) => { var dir = show_select_folder_dialog.end (res); if (dir != null) { var uri = ((!)dir).get_uri (); if (app.music_folder != uri) app.music_folder = uri; picked ((!)dir); } }); } } ================================================ FILE: src/ui/stable-label.vala ================================================ namespace G4 { public enum EllipsizeMode { NONE = Pango.EllipsizeMode.NONE, START = Pango.EllipsizeMode.START, MIDDLE = Pango.EllipsizeMode.MIDDLE, END = Pango.EllipsizeMode.END, MARQUEE } public class StableLabel : Gtk.Widget { private EllipsizeMode _ellipsize = EllipsizeMode.NONE; private Gtk.Label _label = new Gtk.Label (null); private float _label_offset = 0; private int _label_width = 0; construct { _label.xalign = 0; _label.set_parent (this); } ~StableLabel () { _label.unparent (); } public EllipsizeMode ellipsize { get { return _ellipsize; } set { _ellipsize = value; _label.ellipsize = value == EllipsizeMode.MARQUEE ? Pango.EllipsizeMode.NONE : (Pango.EllipsizeMode) value; stop_tick (); } } public string label { get { return _label.label; } set { _label.label = value; stop_tick (); } } public bool marquee { get { return _ellipsize == EllipsizeMode.MARQUEE; } set { ellipsize = value ? EllipsizeMode.MARQUEE : EllipsizeMode.NONE; } } public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (orientation == Gtk.Orientation.VERTICAL) { // Ensure enough space for different text var text = _label.label; _label.label = "A中"; _label.measure (orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); _label.label = text; } else { _label.measure (orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); _label_width = natural; if (marquee) minimum = 0; } } public override void size_allocate (int width, int height, int baseline) { var allocation = Gtk.Allocation (); allocation.x = 0; allocation.y = 0; allocation.width = width; allocation.height = height; _label.allocate_size (allocation, baseline); update_tick_delayed (); } private static float SPACE = 40f; public override void snapshot (Gtk.Snapshot snapshot) { var width = get_width (); var overflow = marquee && width < _label_width; if (overflow) { var height = get_height (); var total_width = _label_width + SPACE; #if GTK_4_10 var mask_width = float.min (width * 0.1f, height * 1.25f); var left_mask = _label_offset < mask_width ? _label_offset : (_label_offset + mask_width > total_width ? 0 : mask_width); var rect = Graphene.Rect (); rect.init (0, 0, left_mask, height); Gsk.ColorStop[] stops = { { 0, color_from_uint (0xff000000u) }, { 1, color_from_uint (0x00000000u) } }; snapshot.push_mask(Gsk.MaskMode.INVERTED_ALPHA); snapshot.append_linear_gradient (rect, rect.get_top_left (), rect.get_top_right (), stops); rect.init (width - mask_width, 0, mask_width, height); snapshot.append_linear_gradient (rect, rect.get_top_right (), rect.get_top_left (), stops); snapshot.pop (); #endif var bounds = Graphene.Rect (); bounds.init (0, 0, width, height); snapshot.push_clip (bounds); var point = Graphene.Point (); point.init (0, 0); if (_label_offset < _label_width) { point.x = - _label_offset; snapshot.translate (point); base.snapshot (snapshot); point.x = - point.x; snapshot.translate (point); } if (_label_offset >= total_width - width) { point.x = - _label_offset + total_width; snapshot.translate (point); base.snapshot (snapshot); point.x = - point.x; snapshot.translate (point); } snapshot.pop (); #if GTK_4_10 snapshot.pop (); // Must call again if snapshot.push_mask() ??? #endif } else { base.snapshot (snapshot); } } private uint _pixels_per_second = 24; private uint _tick_handler = 0; private bool _tick_moving = false; private int64 _tick_start_time = 0; private bool on_tick_callback (Gtk.Widget widget, Gdk.FrameClock clock) { if (_tick_moving) { var now = get_monotonic_time (); var elapsed = (now - _tick_start_time) / 1e6f; _label_offset = elapsed * _pixels_per_second; if (_label_offset > _label_width + SPACE) { stop_tick (); update_tick_delayed (); } queue_draw (); } return true; } private void stop_tick () { if (_tick_handler != 0) { remove_tick_callback (_tick_handler); _tick_handler = 0; } if (_timer_id != 0) { Source.remove (_timer_id); _timer_id = 0; } _label_offset = 0; _tick_moving = false; } private void update_tick () { var need_tick = marquee && get_width () < _label_width; if (need_tick && _tick_handler == 0) { _tick_handler = add_tick_callback (on_tick_callback); _tick_moving = _tick_handler != 0; _tick_start_time = get_monotonic_time (); } else if (!need_tick && _tick_handler != 0) { stop_tick (); } } private static uint TICK_WAIT = 3000; private uint _timer_id = 0; private void update_tick_delayed () { if (_timer_id == 0) { _timer_id = run_timeout_once (TICK_WAIT, () => { _timer_id = 0; update_tick (); }); } } } } ================================================ FILE: src/ui/store-panel.vala ================================================ namespace G4 { namespace SearchMode { public const uint ANY = 0; public const uint ALBUM = 1; public const uint ARTIST = 2; public const uint TITLE = 3; } public const string[] SORT_MODE_ICONS = { "media-optical-cd-audio-symbolic", // ALBUM "system-users-symbolic", // ARTIST "avatar-default-symbolic", // ARTIST_ALBUM "folder-music-symbolic", // TITLE "document-open-recent-symbolic", // RECENT "media-playlist-shuffle-symbolic", // SHUFFLE }; namespace StackFlags { public const uint FIRST = 1; public const uint ARTISTS = 1; public const uint ALBUMS = 2; public const uint PLAYLISTS = 3; public const uint LAST = 4; } [GtkTemplate (ui = "/com/github/neithern/g4music/gtk/store-panel.ui")] public class StorePanel : Gtk.Box, SizeWatcher { [GtkChild] public unowned Gtk.HeaderBar header_bar; [GtkChild] public unowned Gtk.Label indicator; [GtkChild] private unowned Gtk.MenuButton sort_btn; [GtkChild] private unowned Gtk.ToggleButton search_btn; [GtkChild] private unowned Gtk.SearchBar search_bar; [GtkChild] private unowned Gtk.SearchEntry search_entry; [GtkChild] private unowned Gtk.Stack stack_view; private Stack _album_stack = new Stack (); private Stack _artist_stack = new Stack (); private Stack _playlist_stack = new Stack (); private MiniBar _mini_bar = new MiniBar (); private Gtk.StackSwitcher _switcher_top = new Gtk.StackSwitcher (); private Gtk.StackSwitcher _switcher_btm = new Gtk.StackSwitcher (); private Application _app; private MusicList _album_list; private MusicList _artist_list; private MusicList _current_list; private MainMusicList _main_list; private MusicList _playlist_list; private MusicLibrary _library; private string? _library_uri = null; private Gdk.Paintable _loading_paintable; private uint _search_mode = SearchMode.ANY; private string _search_text = ""; private bool _size_allocated = false; private uint _sort_mode = -1; private bool _updating_store = false; public StorePanel (Application app, Window win, Leaflet leaflet) { _app = app; _library = app.loader.library; margin_bottom = 6; var thumbnailer = app.thumbnailer; thumbnailer.pango_context = get_pango_context (); thumbnailer.scale_factor = this.scale_factor; _loading_paintable = thumbnailer.create_simple_text_paintable ("...", Thumbnailer.ICON_SIZE); search_btn.toggled.connect (on_search_btn_toggled); search_bar.key_capture_widget = win.content; search_entry.search_changed.connect (on_search_text_changed); _main_list = create_main_music_list (); _main_list.data_store = _app.music_queue; _app.current_list = _main_list.filter_model; _current_list = _main_list; stack_view.add_titled (_main_list, PageName.PLAYING, _("Playing")).icon_name = "user-home-symbolic"; _artist_list = create_artist_list (); _artist_stack.add (_artist_list, PageName.ARTIST); _artist_stack.bind_property ("visible-child", this, "visible-child"); stack_view.add_titled (_artist_stack.widget, PageName.ARTIST, _("Artists")).icon_name = "system-users-symbolic"; _album_list = create_album_list (); _album_stack.add (_album_list, PageName.ALBUM); _album_stack.bind_property ("visible-child", this, "visible-child"); stack_view.add_titled (_album_stack.widget, PageName.ALBUM, _("Albums")).icon_name = "drive-multidisk-symbolic"; _playlist_list = create_playlist_list (); _playlist_stack.add (_playlist_list, PageName.PLAYLIST); _playlist_stack.bind_property ("visible-child", this, "visible-child"); stack_view.add_titled (_playlist_stack.widget, PageName.PLAYLIST, _("Playlists")).icon_name = "view-list-symbolic"; stack_view.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT; stack_view.bind_property ("visible-child", this, "visible-child"); var mini_revealer = new Gtk.Revealer (); mini_revealer.child = _mini_bar; mini_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_UP; _mini_bar.activated.connect (leaflet.push); append (mini_revealer); leaflet.bind_property ("folded", mini_revealer, "reveal-child", BindingFlags.SYNC_CREATE); leaflet.bind_property ("folded", header_bar, "show-title-buttons"); var top_revealer = new NarrowBar (); top_revealer.child = _switcher_top; _switcher_top.stack = stack_view; fix_switcher_style (_switcher_top); header_bar.pack_end (top_revealer); var btm_revealer = new Gtk.Revealer (); btm_revealer.child = _switcher_btm; btm_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_UP; append (btm_revealer); top_revealer.bind_property ("reveal", btm_revealer, "reveal-child", BindingFlags.INVERT_BOOLEAN); _switcher_btm.margin_top = 2; _switcher_btm.margin_start = 6; _switcher_btm.margin_end = 6; _switcher_btm.stack = stack_view; fix_switcher_style (_switcher_btm); app.music_changed.connect (on_music_changed); app.music_library_changed.connect (on_music_library_changed); app.playlist_added.connect (on_playlist_added); app.thumbnail_changed.connect (on_thumbnail_changed); var settings = app.settings; settings.bind ("sort-mode", this, "sort-mode", SettingsBindFlags.DEFAULT); _library_uri = settings.get_string ("library-uri"); initialize_library_page (); } public MusicList current_list { get { return _current_list; } } public bool modified { get { return _current_list.modified; } set { indicator.visible = _current_list.modified; root.action_set_enabled (ACTION_WIN + ACTION_SAVE_LIST, _current_list.modified); } } public uint sort_mode { get { return _sort_mode; } set { _sort_mode = value; if (value < SORT_MODE_ICONS.length) sort_btn.set_icon_name (SORT_MODE_ICONS[value]); if (_main_list.get_height () > 0) _main_list.create_factory (); } } public Gtk.Widget visible_child { set { if (_size_allocated) { update_visible_stack (); } save_if_modified (true); if (value == stack_view.visible_child) { var stack = get_current_stack (); if (stack != null) value = ((!)stack).visible_child; } if (value is MusicList) { var list = _current_list = (MusicList) value; indicator.visible = _current_list.modified; sort_btn.visible = _current_list == _main_list; _search_mode = SearchMode.ANY; on_search_btn_toggled (); var scroll = !_overlayed_lists.remove (list); set_to_current_music (scroll); } save_current_page (); } } public void first_allocated () { // Delay set model after the window size allocated to avoid showing slowly _size_allocated = true; } public void remove_from_list (Music music) { uint position = -1; if (_current_list.data_store.find (music, out position)) { _current_list.data_store.remove (position); _current_list.modified = true; } } public bool save_if_modified (bool prompt = true, VoidFunc? done = null) { if (_current_list != _main_list) { _main_list.save_if_modified.begin (false, (obj, res) => _main_list.save_if_modified.end (res)); } if (_current_list.modified) { _current_list.save_if_modified.begin (prompt, (obj, res) => { var ret = _current_list.save_if_modified.end (res); if (ret != Result.FAILED) { _current_list.modified = false; if (done != null) ((!)done) (); } }); return true; } return false; } public void set_mini_cover (Gdk.Paintable? cover) { _mini_bar.cover = cover; } public void size_to_change (int width, int height) { } public void start_search (string text, uint mode = SearchMode.ANY) { switch (mode) { case SearchMode.ALBUM: stack_view.visible_child = _album_stack.widget; break; case SearchMode.ARTIST: stack_view.visible_child = _artist_stack.widget; break; case SearchMode.TITLE: stack_view.visible_child = _main_list; break; } #if GTK_4_10 var delay = search_entry.search_delay; search_entry.search_delay = 0; run_idle_once (() => search_entry.search_delay = delay); #endif search_entry.text = text; search_entry.select_region (0, -1); search_btn.active = true; _search_mode = mode; } public bool toggle_search () { search_btn.active = ! search_btn.active; return search_btn.active; } private void bind_music_list_properties (MusicList list, bool editable = false) { _app.settings.bind ("compact-playlist", list, "compact-list", SettingsBindFlags.DEFAULT); _app.settings.bind ("single-click-activate", list, "single-click-activate", SettingsBindFlags.DEFAULT); if (list.item_type != typeof (Music)) _app.settings.bind ("grid-mode", list, "grid-mode", SettingsBindFlags.DEFAULT); if (editable) list.bind_property ("modified", this, "modified"); } private MusicList create_album_list (Artist? artist = null) { var list = new MusicList (_app, typeof (Album), artist); list.item_activated.connect ((position, obj) => create_stack_page (artist, obj as Album)); list.item_binded.connect ((item) => { var cell = (MusicWidget) item.child; var album = (Album) item.item; var album_artist = album.album_artist; cell.music = album; cell.paintable = _loading_paintable; cell.title = album.album; var subtitle = album.get_date_string (); if (artist == null && album_artist.length > 0) subtitle = album_artist + " " + subtitle; cell.subtitle = subtitle.length > 0 ? subtitle : " "; }); bind_music_list_properties (list); return list; } private MusicList create_artist_list () { var list = new MusicList (_app, typeof (Artist)); list.item_activated.connect ((position, obj) => create_stack_page (obj as Artist)); list.item_binded.connect ((item) => { var cell = (MusicWidget) item.child; var artist = (Artist) item.item; cell.cover.ratio = 0.5; cell.music = artist; cell.paintable = _loading_paintable; cell.title = artist.artist; cell.subtitle = ""; }); bind_music_list_properties (list); return list; } private MusicList create_music_list (Album album, bool from_artist = false) { var is_playlist = album is Playlist; var is_artist_playlist = is_playlist && from_artist; var sort_mode = is_artist_playlist ? SortMode.ALBUM : SortMode.TITLE; var list = new MusicList (_app, typeof (Music), album, is_playlist); list.item_activated.connect ((position, obj) => play_current_list ((int) position)); list.item_binded.connect ((item) => { var entry = (MusicEntry) item.child; var music = (Music) item.item; entry.paintable = _loading_paintable; entry.set_titles (music, sort_mode); }); bind_music_list_properties (list, is_playlist); return list; } private MainMusicList create_main_music_list () { var list = new MainMusicList (_app); list.item_activated.connect ((position, obj) => play_current_list ((int) position)); list.item_binded.connect ((item) => { var entry = (MusicEntry) item.child; var music = (Music) item.item; entry.paintable = _loading_paintable; entry.set_titles (music, _sort_mode); }); bind_music_list_properties (list, true); return list; } private MusicList create_playlist_list () { var list = new MusicList (_app, typeof (Playlist)); list.item_activated.connect ((position, obj) => create_stack_page (null, obj as Playlist)); list.item_binded.connect ((item) => { var cell = (MusicWidget) item.child; var playlist = (Playlist) item.item; cell.music = playlist; cell.paintable = _loading_paintable; cell.title = playlist.title; }); bind_music_list_properties (list); return list; } private Gtk.Box create_title_bar (string title, string? icon_name = null, Playlist? plist = null) { var label = new Gtk.Label (title); label.ellipsize = Pango.EllipsizeMode.MIDDLE; var icon = new Gtk.Image.from_icon_name (icon_name); var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); box.halign = Gtk.Align.CENTER; box.hexpand = true; box.append (icon); box.append (label); if (plist != null && ((!)plist).list_uri.length > 0) { var playlist = (!)plist; var entry = new Gtk.Entry (); entry.max_width_chars = 1024; entry.text = title; entry.visible = false; entry.activate.connect (() => { entry.visible = false; label.visible = true; var text = entry.text; if (text.length > 0 && text != playlist.title) { _app.rename_playlist_async.begin (playlist, text, (obj, res) => { var ret = _app.rename_playlist_async.end (res); if (ret) label.label = playlist.title; }); } }); var event = new Gtk.EventControllerKey (); event.key_pressed.connect ((keyval, keycode, state) => { if (keyval == Gdk.Key.Escape) { entry.visible = false; label.visible = true; return true; } return false; }); entry.add_controller (event); make_widget_clickable (label).released.connect (() => { entry.text = label.label; entry.visible = true; entry.grab_focus (); label.visible = false; }); box.append (entry); } var header = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); header.hexpand = true; header.add_css_class ("flat"); header.add_css_class ("toolbar"); header.append (box); return header; } private GenericSet _overlayed_lists = new GenericSet (direct_hash, direct_equal); private void create_stack_page (Artist? artist, Album? album = null) { var album_mode = album != null; var artist_mode = artist != null; var playlist_mode = album is Playlist; var mlist = album_mode ? create_music_list ((!)album, artist_mode) : create_album_list (artist); mlist.update_store (); var icon_name = (album is Playlist) ? "x-office-document-symbolic" : (album_mode ? "media-optical-cd-audio-symbolic" : "avatar-default-symbolic"); var title = (album_mode ? album?.title : artist?.title) ?? ""; var header = create_title_bar (title, icon_name, album as Playlist); mlist.prepend (header); var stack = artist_mode ? _artist_stack : playlist_mode ? _playlist_stack : _album_stack; var back_btn = new Gtk.Button.from_icon_name ("go-previous-symbolic"); back_btn.tooltip_text = _("Back"); back_btn.clicked.connect (stack.pop); header.prepend (back_btn); var key_length = album?.album_key?.length ?? 0; if (artist_mode || key_length > 0) { var split_btn = new Adw.SplitButton (); split_btn.icon_name = "media-playback-start-symbolic"; split_btn.tooltip_text = _("Play"); split_btn.clicked.connect (() => open_page (build_library_uri (artist, album), true)); if (artist != null) split_btn.menu_model = (album == null || album is Playlist) ? create_menu_for_artist ((!)artist) : create_menu_for_album ((!)album); else if (album != null) split_btn.menu_model = create_menu_for_album ((!)album); (split_btn.menu_model as Menu)?.remove (0); // Play header.append (split_btn); } if (stack.animate_transitions && stack.visible_child == _current_list) _overlayed_lists.add (_current_list); stack.add (mlist, album_mode ? album?.album_key : artist?.artist_name); } private Stack? get_current_stack () { var child = stack_view.visible_child; if (_artist_stack.widget == child) return _artist_stack; else if (_album_stack.widget == child) return _album_stack; else if (_playlist_stack.widget == child) return _playlist_stack; return null; } private void initialize_library_page () { if (_library_uri != null) { open_page ((!)_library_uri); if (!_library.empty) { _library_uri = null; if (_current_list.playable) _album_key_of_list = _current_list.music_node?.album_key; set_to_current_music (); } } } private void save_current_page () { var paths = new GenericArray (4); var stack = get_current_stack (); if (stack != null) { ((!)stack).get_visible_names (paths); } else { paths.add (stack_view.get_visible_child_name () ?? ""); } var uri = build_library_uri_from_sa (paths.data); _app.settings.set_string ("library-uri", uri); _album_key_of_list = _current_list.music_node?.album_key; } public bool open_page (string uri, bool play_now = false, bool shuffle = false) { string? ar = null, al = null, pl = null, page = null; if (parse_library_uri (uri, out ar, out al, out pl, out page)) { stack_view.transition_type = Gtk.StackTransitionType.NONE; stack_view.visible_child_name = (!)page; stack_view.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT; var stk = get_current_stack (); if (stk != null) { var stack = (!)stk; stack.animate_transitions = false; Artist? artist = null; Album? album = null; if (ar != null) { artist = _library.get_artist ((!)ar); if (artist != null) { if (stack.get_child_by_name (((!)artist).artist) == null) { create_stack_page (artist); } if (al != null) { if (((!)al).length > 0) album = ((!)artist)[(!)al]; else album = ((!)artist).to_playlist (); } } } else if (al != null) { album = _library.get_album ((!)al); } else if (pl != null) { album = _library.get_playlist ((!)pl); } if (album != null && stack.get_child_by_name (((!)album).album_key) == null) { if ((stack.visible_child as MusicList)?.playable ?? false) stack.pop (); create_stack_page (artist, album); } ((!)stack).animate_transitions = true; if (album != null) { if (shuffle) { sort_music_store (_current_list.data_store, SortMode.SHUFFLE); } else { ((!)album).overwrite_to (_current_list.data_store); } if (play_now) { play_current_list (); } } return true; } } return false; } public int open_next_playable_page () { var stk = get_current_stack (); if (stk != null && !_updating_store) { var stack = (!)stk; if (_current_list.music_node is Playlist) { pop_page_without_animation (stack); if (_current_list.music_node is Artist) pop_page_without_animation (stack); } else if (_current_list.set_to_current_item (false) >= (int) _current_list.visible_count - 1) { pop_page_without_animation (stack); if (_current_list.set_to_current_item (false) >= (int) _current_list.visible_count - 1) { pop_page_without_animation (stack); } } if (!_current_list.playable) { var index = _current_list.set_to_current_item (); _current_list.activate_item (index < (int) _current_list.visible_count - 1 ? index + 1 : 0); if (!_current_list.playable) _current_list.activate_item (0); } if (_current_list.playable) { var playlist = _current_list.get_as_playlist (); _app.insert_to_queue (playlist); } } var index = _app.current_item + 1; return index < (int) _app.current_list.get_n_items () ? index : 0; } public void open_playing_page () { if (stack_view.visible_child != _main_list) stack_view.visible_child = _main_list; else set_to_current_music (); } private void on_music_changed (Music? music) { if (_current_list.playable) { _current_list.current_node = music; } else if (_current_list.item_type == typeof (Artist)) { var artist_name = music?.artist_name ?? ""; _current_list.current_node = _library.get_artist (artist_name); } else if (_current_list.item_type == typeof (Album)) { var album = music?.album_key ?? ""; var artist = _current_list.music_node as Artist; _current_list.current_node = artist != null ? ((!)artist)[album] : _library.get_album (album); } else if (_current_list.item_type == typeof (Playlist)) { if (_album_key_of_list != null) _current_list.current_node = _library.get_playlist ((!)_album_key_of_list); } _mini_bar.title = music?.title ?? ""; var scroll = _current_list.dropping_item == -1 && !_current_list.multi_selection; _current_list.set_to_current_item (scroll); } private Gtk.Bitset _changing_stacks = new Gtk.Bitset.empty (); private void on_music_library_changed (bool external) { _main_list.modified |= _app.list_modified; if (external) { _changing_stacks.add_range (StackFlags.FIRST, StackFlags.LAST - StackFlags.FIRST); if (_size_allocated) { update_visible_stack (); initialize_library_page (); } } } private void on_playlist_added (Playlist playlist) { var list = _playlist_stack.visible_child as MusicList; var node = list?.music_node; if (strcmp (playlist.list_uri, (node as Playlist)?.list_uri) == 0) { playlist.overwrite_to (((!)list).data_store); } var arr = new GenericArray (1); arr.add (playlist); uint position = -1; merge_items_to_store (_playlist_list.data_store, arr, ref position); sort_music_store (_playlist_list.data_store, SortMode.TITLE); } private void on_search_btn_toggled () { if (search_btn.active) { search_entry.grab_focus (); } on_search_text_changed (); } private bool on_search_match (Object obj) { unowned var music = (Music) obj; unowned var text = _search_text; switch (_search_mode) { case SearchMode.ALBUM: return text.match_string (music.album, true); case SearchMode.ARTIST: return text.match_string (music.artist, true) || text.match_string (music.album_artist, true) || ((music as Artist)?.find_by_partial_artist (text) != null); case SearchMode.TITLE: return text.match_string (music.title, true); default: return text.match_string (music.album, true) || text.match_string (music.album_artist, true) || text.match_string (music.artist, true) || text.match_string (music.title, true); } } private void on_search_text_changed () { _search_text = search_entry.text; parse_search_mode (ref _search_text, ref _search_mode); if (_current_list == _album_list) { _search_mode = SearchMode.ALBUM; } else if (_current_list == _artist_list) { _search_mode = SearchMode.ARTIST; } var model = _current_list.filter_model; if (search_btn.active && model.get_filter () == null) { model.set_filter (new Gtk.CustomFilter (on_search_match)); } else if (!search_btn.active && model.get_filter () != null) { model.set_filter (null); } model.get_filter ()?.changed (Gtk.FilterChange.DIFFERENT); } private void on_thumbnail_changed (Music music, Gdk.Paintable paintable) { _current_list.update_item_cover (music, paintable); } private string? _album_key_of_list = null; private void play_current_list (int index = 0) { if (_app.current_list == _current_list.filter_model) { _app.current_item = index; } else if (_current_list.playable) { var playlist = _current_list.get_as_playlist (); _app.current_item = _app.insert_after_current (playlist) + index; } if (!_app.player.playing) { _app.player.play (); } _album_key_of_list = _current_list.music_node?.album_key; } private void pop_page_without_animation (Stack stack) { var animate = stack.animate_transitions; stack.animate_transitions = false; stack.pop (); stack.animate_transitions = animate; } private void set_to_current_music (bool scroll = true) { on_music_changed (_app.current_music); _current_list.set_to_current_item (scroll); } private void update_stack_pages (Stack stack) { var children = stack.get_children (); for (var i = children.length - 1; i >= 0; i--) { var mlist = (MusicList) children[i]; if (mlist.music_node != null && mlist.update_store () == 0) pop_page_without_animation (stack); } } private void update_visible_stack () { _updating_store = true; var child = stack_view.visible_child; if (child == _album_stack.widget && _changing_stacks.remove (StackFlags.ALBUMS)) { update_stack_pages (_album_stack); _library.overwrite_albums_to (_album_list.data_store); } else if (child == _artist_stack.widget && _changing_stacks.remove (StackFlags.ARTISTS)) { update_stack_pages (_artist_stack); _library.overwrite_artists_to (_artist_list.data_store); } else if (child == _playlist_stack.widget && _changing_stacks.remove (StackFlags.PLAYLISTS)) { update_stack_pages (_playlist_stack); var text = _("No playlist found in %s").printf (get_display_name (_app.music_folder)); _library.overwrite_playlists_to (_playlist_list.data_store); _playlist_list.set_empty_text (text); } _updating_store = false; } } public void fix_switcher_style (Gtk.StackSwitcher switcher) { var layout = switcher.get_layout_manager () as Gtk.BoxLayout; layout?.set_spacing (4); switcher.remove_css_class ("linked"); for (var child = switcher.get_first_child (); child != null; child = child?.get_next_sibling ()) { child?.add_css_class ("flat"); ((!)child).width_request = 48; } } public void parse_search_mode (ref string text, ref uint mode) { if (text.ascii_ncasecmp ("album:", 6) == 0) { mode = SearchMode.ALBUM; text = text.substring (6); } else if (text.ascii_ncasecmp ("artist:", 7) == 0) { mode = SearchMode.ARTIST; text = text.substring (7); } else if (text.ascii_ncasecmp ("title:", 6) == 0) { mode = SearchMode.TITLE; text = text.substring (6); } } } ================================================ FILE: src/ui/taglist-dialog.vala ================================================ namespace G4 { namespace TagGroup { public const int BASIC = 1; public const int SORT = 2; public const int FORMAT = 3; public const int OTHER = 4; } public class TagListDialog : Dialog { public struct TagOrder { unowned string tag; int order; } public class TagItem : Object { public int group; public string tag; public string value; public string description; private string _key; private int _order; public TagItem (string t, string v) { tag = embellish_tag_name (t); value = v; description = Gst.Tags.get_description (t) ?? ""; _key = tag.collate_key_for_filename (); unowned string orig_key; if (ORDERS.lookup_extended (_key, out orig_key, out _order)) { group = TagGroup.BASIC; } else { _order = int.MAX >> 1; if (t.contains ("bitrate") || t.contains ("channel") || t.contains ("crc") || t.contains ("code") || t.contains ("format")) { group = TagGroup.FORMAT; } else if (t.contains ("sortname")) { group = TagGroup.SORT; } else { group = TagGroup.OTHER; } } } public static int compare_by_name (TagItem ti1, TagItem ti2) { int ret = ti1.group - ti2.group; if (ret == 0) ret = ti1._order - ti2._order; if (ret == 0) ret = strcmp (ti1._key, ti2._key); return ret; } } public const string GST_DOMAIN = "gstreamer-1.0"; public static HashTable ORDERS = new HashTable (str_hash, str_equal); static construct { Intl.bindtextdomain (GST_DOMAIN, null); Intl.bind_textdomain_codeset (GST_DOMAIN, "UTF-8"); TagOrder [] tag_orders = { { Gst.Tags.TITLE, 1 }, { Gst.Tags.ARTIST, 2 }, { Gst.Tags.ALBUM, 3 }, { Gst.Tags.ALBUM_ARTIST, 4 }, { Gst.Tags.COMPOSER, 5 }, { Gst.Tags.GENRE, 6 }, { Gst.Tags.DATE_TIME, 7 }, { Gst.Tags.TRACK_NUMBER, 8 }, { Gst.Tags.ALBUM_VOLUME_NUMBER, 9 }, { Gst.Tags.COMMENT, int.MAX - 1 }, { Gst.Tags.EXTENDED_COMMENT, int.MAX } }; foreach (var to in tag_orders) { ORDERS.insert (embellish_tag_name (to.tag).collate_key_for_filename (), to.order); } } public static string embellish_tag_name (string name) { var text = dgettext (GST_DOMAIN, Gst.Tags.get_nick (name) ?? name); var sb = new StringBuilder (); var arr = text.split (" "); var count = arr.length; for (var i = 0; i < count; i++) { var str = arr[i]; var first = true; var next = 0; unichar c = 0; while (str.get_next_char (ref next, out c)) { var s = c.to_string (); if (first) { first = false; sb.append (s.up ()); } else { sb.append (s); } } if (i < count - 1) sb.append_c (' '); } return sb.str; } private File file; private GenericArray items = new GenericArray(); private Gtk.Button copy_btn = new Gtk.Button.from_icon_name ("edit-copy-symbolic"); private Gtk.Box group = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); private Gtk.Spinner spinner = new Gtk.Spinner (); public TagListDialog (string uri, Gst.TagList? tags) { var content = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); this.child = content; this.file = File.new_for_uri (uri); var header = new Gtk.HeaderBar (); header.show_title_buttons = true; header.title_widget = new Gtk.Label (null); header.add_css_class ("flat"); content.append (header); copy_btn.clicked.connect (copy_to_clipboard); copy_btn.tooltip_text = _("Copy"); header.pack_start (copy_btn); spinner.margin_start = 6; header.pack_start (spinner); var scroll_view = new Gtk.ScrolledWindow (); scroll_view.hscrollbar_policy = Gtk.PolicyType.NEVER; scroll_view.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; scroll_view.propagate_natural_height = true; scroll_view.vexpand = true; content.append (scroll_view); var viewport = new Gtk.Viewport (null, scroll_view.vadjustment); viewport.child = group; scroll_view.child = viewport; if (tags != null) { load_tags ((!)tags); } else { laod_tags_async.begin (uri, (obj, res) => laod_tags_async.end (res)); } } private Gtk.ListBox create_list_box (Gtk.ListBox? previous) { var box = new Gtk.ListBox (); box.margin_start = 16; box.margin_end = 16; box.margin_top = previous == null ? 8 : 0; box.margin_bottom = 16; box.selection_mode = Gtk.SelectionMode.NONE; box.add_css_class ("boxed-list"); return box; } private void copy_to_clipboard () { var path = file.get_path () ?? file.get_parse_name (); var sb = new StringBuilder (path); sb.append_c ('\n'); foreach (var ti in items) { sb.append (ti.tag); sb.append (ti.value.contains ("\n") ? ":\n" : "="); sb.append (ti.value); sb.append_c ('\n'); } get_clipboard ().set_text (sb.str); } private async void laod_tags_async (string uri) { child.height_request = 480; copy_btn.sensitive = false; spinner.start (); var tags = yield run_async (() => parse_gst_tags (file)); if (tags != null) { load_tags ((!)tags); } copy_btn.sensitive = true; spinner.stop (); } private void load_tags (Gst.TagList tags) { TagItem? tag_track_count = null; TagItem? tag_track_number = null; TagItem? tag_volumn_count = null; TagItem? tag_volumn_number = null; var count = tags.n_tags (); for (var i = 0; i < count; i++) { var tag = tags.nth_tag_name (i); var values = new GenericArray (4); get_one_tag (tags, tag, values); if (values.length > 0) { var sb = new StringBuilder (); var size = values.length; for (var j = 0; j < size; j++) { sb.append (values[j]); if (j != size - 1) sb.append_c ('\n'); } var ti = new TagItem (tag, sb.str); if (tag == Gst.Tags.TRACK_COUNT) tag_track_count = ti; else if (tag == Gst.Tags.TRACK_NUMBER) tag_track_number = ti; else if (tag == Gst.Tags.ALBUM_VOLUME_COUNT) tag_volumn_count = ti; else if (tag == Gst.Tags.ALBUM_VOLUME_NUMBER) tag_volumn_number = ti; else items.add (ti); } } if (tag_track_number != null) { if (tag_track_count != null) ((!)tag_track_number).value += "/" + ((!)tag_track_count).value; items.add ((!)tag_track_number); } if (tag_volumn_number != null) { if (tag_volumn_count != null) ((!)tag_volumn_number).value += "/" + ((!)tag_volumn_count).value; items.add ((!)tag_volumn_number); } items.sort (TagItem.compare_by_name); var tag_group = -1; Gtk.ListBox? list_box = null; foreach (var ti in items) { var row = new Adw.ActionRow (); row.title = ti.tag; row.tooltip_text = dgettext (GST_DOMAIN, ti.description); #if ADW_1_2 row.use_markup = false; #endif #if ADW_1_3 row.subtitle_selectable = true; #endif row.subtitle = ti.value; row.add_css_class ("property"); if (tag_group != ti.group || list_box == null) { tag_group = ti.group; list_box = create_list_box (list_box); group.append ((!)list_box); } list_box?.append (row); } } } } ================================================ FILE: src/ui/volume-button.vala ================================================ namespace G4 { public class VolumeButton : Gtk.ScaleButton, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { public static double EPSILON = 1e-10; public static string[] ICONS = { "audio-volume-muted-symbolic", "audio-volume-high-symbolic", "audio-volume-low-symbolic", "audio-volume-medium-symbolic", }; construct { var adj = adjustment; adj.lower = 0; adj.upper = 1.0; adj.page_increment = 0.1; adj.step_increment = 0.1; icons = ICONS; query_tooltip.connect (on_query_tooltip); value_changed.connect (on_value_changed); tooltip_text = get_volume_text (); } private string get_volume_text () { var adj = adjustment; var value = get_value (); var percent = (int) (100 * value / (adj.upper - adj.lower) + 0.5); /* Translators: Current volume percent: 0~100% */ return _("Volume %d%%").printf (percent); } private bool on_query_tooltip (int x, int y, bool keyboard_mode, Gtk.Tooltip tooltip) { tooltip.set_text (get_volume_text ()); return true; } private void on_value_changed (double value) { trigger_tooltip_query (); } } } ================================================ FILE: src/ui/window.vala ================================================ namespace G4 { public class Window : Adw.ApplicationWindow { private Adw.ToastOverlay _toast = new Adw.ToastOverlay (); private Leaflet _leaflet = new Leaflet (); private Gtk.ProgressBar _progress_bar = new Gtk.ProgressBar (); private PlayPanel _play_panel; private StorePanel _store_panel; private int _blur_size = 512; private uint _bkgnd_blur = BlurMode.ALWAYS; private CrossFadePaintable _bkgnd_paintable = new CrossFadePaintable (); private Gdk.Paintable? _cover_paintable = null; public Window (Application app) { this.application = app; this.icon_name = app.application_id; this.title = app.name; this.width_request = ContentWidth.MIN; this.close_request.connect (on_close_request); var overlay = new Gtk.Overlay (); this.content = overlay; overlay.child = _toast; _toast.child = _leaflet; ActionEntry[] action_entries = { { ACTION_BUTTON, button_command, "s" }, { ACTION_REMOVE, remove_from_list, "s" }, { ACTION_SAVE_LIST, save_list }, { ACTION_SEARCH, search_by, "as" }, { ACTION_SELECT, start_select }, { ACTION_TOGGLE_SEARCH, toggle_search }, }; add_action_entries (action_entries, this); _progress_bar.hexpand = true; _progress_bar.pulse_step = 0.02; _progress_bar.sensitive = false; _progress_bar.visible = false; _progress_bar.add_css_class ("osd"); overlay.add_overlay (_progress_bar); overlay.get_child_position.connect (on_overlay_child_position); app.loader.loading_changed.connect (on_loading_changed); _bkgnd_paintable.queue_draw.connect (this.queue_draw); _store_panel = new StorePanel (app, this, _leaflet); _play_panel = new PlayPanel (app, this, _leaflet); _play_panel.cover_changed.connect (on_cover_changed); _leaflet.content = _play_panel; _leaflet.sidebar = _store_panel; setup_drop_target (); setup_focus_controller (); var settings = app.settings; settings.bind ("leaflet-mode", _leaflet, "visible-mode", SettingsBindFlags.DEFAULT); settings.bind ("maximized", this, "maximized", SettingsBindFlags.DEFAULT); settings.bind ("width", this, "default-width", SettingsBindFlags.DEFAULT); settings.bind ("height", this, "default-height", SettingsBindFlags.DEFAULT); settings.bind ("blur-mode", this, "blur-mode", SettingsBindFlags.DEFAULT); } public uint blur_mode { get { return _bkgnd_blur; } set { _bkgnd_blur = value; if (get_height () > 0) update_background (); } } public bool focused_visible { get { return focus_visible; } set { if (!value) focus_to_play_later (); } } public Gtk.Widget focused_widget { owned get { return focus_widget; } set { if (!(value is Gtk.Editable)) focus_to_play_later (2000); } } public override void snapshot (Gtk.Snapshot snapshot) { _bkgnd_paintable.snapshot (snapshot, get_width (), get_height ()); base.snapshot (snapshot); } public void open_page (string uri, bool play_now = false, bool shuffle = false) { _store_panel.open_page (uri, play_now, shuffle); if (_leaflet.folded) { _leaflet.pop (); } } public int open_next_playable_page () { return _store_panel.open_next_playable_page (); } public void open_playing_page () { _store_panel.open_playing_page (); if (_leaflet.folded) { _leaflet.pop (); } } public void show_toast (string message, string? uri = null) { var toast = new Adw.Toast (message); if (uri != null) { toast.action_name = ACTION_APP + ACTION_SHOW_FILE; toast.action_target = new Variant.string ((!)uri); toast.button_label = _("Show"); } _toast.add_toast (toast); } public void start_search (string text, uint mode = SearchMode.ANY) { _store_panel.start_search (text, mode); if (_leaflet.folded) { _leaflet.pop (); } } private void focus_to_play_later (int delay = 100) { run_timeout_once (delay, () => { if (!focus_visible && !(focus_widget is Gtk.Editable)) { var button = find_button_by_action_name (_leaflet, ACTION_APP + ACTION_PLAY_PAUSE); button?.grab_focus (); } }); } private bool on_close_request () { var app = (Application) application; if (app.player.playing && app.settings.get_boolean ("play-background")) { app.request_background (); this.visible = false; return true; } if (_store_panel.save_if_modified (true, close)) { present (); return true; } return false; } private Adw.Animation? _fade_animation = null; private void on_cover_changed (Music? music, CrossFadePaintable cover) { var paintable = cover.paintable; while (paintable is BasePaintable) { paintable = (paintable as BasePaintable)?.paintable; } _cover_paintable = paintable; var app = (Application) application; var mini_cover = music != null ? (app.thumbnailer.find ((!)music) ?? _cover_paintable) : app.icon; _store_panel.set_mini_cover (mini_cover); update_background (); var target = new Adw.CallbackAnimationTarget ((value) => { _bkgnd_paintable.fade = value; cover.fade = value; }); _fade_animation?.pause (); _fade_animation = new Adw.TimedAnimation (this, 1 - cover.fade, 0, 800, target); ((!)_fade_animation).done.connect (() => { _bkgnd_paintable.previous = null; cover.previous = null; _fade_animation = null; }); _fade_animation?.play (); } private bool on_file_dropped (Value value, double x, double y) { var files = get_dropped_files (value); var app = (Application) application; app.open_files_async.begin (files, -1, app.current_music == null, (obj, res) => app.open_files_async.end (res)); return true; } private bool _loading = false; private uint _tick_handler = 0; private void on_loading_changed (bool loading) { root.action_set_enabled (ACTION_APP + ACTION_RELOAD, !loading); _loading = loading; if (loading) { run_timeout_once (100, () => _progress_bar.visible = _loading); } else { _progress_bar.visible = _loading; } if (loading && _tick_handler == 0) { _tick_handler = add_tick_callback (on_loading_tick_callback); } else if (!loading && _tick_handler != 0) { remove_tick_callback (_tick_handler); _tick_handler = 0; } } private bool on_loading_tick_callback (Gtk.Widget widget, Gdk.FrameClock clock) { var app = (Application) application; var fraction = app.loader.loading_progress; if (fraction > 0) _progress_bar.fraction = fraction; else _progress_bar.pulse (); return true; } private bool on_overlay_child_position (Gtk.Widget widget, out Gdk.Rectangle rect) { rect = Gdk.Rectangle (); rect.x = rect.y = rect.width = rect.height = 0; if (widget == _progress_bar) { rect.y = _store_panel.header_bar.get_height (); rect.width = _store_panel.get_width (); } return true; } private void setup_drop_target () { // Hack: when drag a folder from nautilus, // the value is claimed as GdkFileList in accept(), // but the value can't be convert as GdkFileList in drop(), // so use STRING type to get the file/folder path. var target = new Gtk.DropTarget (Type.INVALID, Gdk.DragAction.COPY | Gdk.DragAction.LINK); target.set_gtypes ({ Type.STRING, typeof (Gdk.FileList) }); target.accept.connect ((drop) => drop.formats.contain_gtype (typeof (Gdk.FileList)) && !drop.formats.contain_gtype (typeof (Playlist))); #if GTK_4_10 target.drop.connect (on_file_dropped); #else target.on_drop.connect (on_file_dropped); #endif this.content.add_controller (target); } private void setup_focus_controller () { var controller = new Gtk.EventControllerFocus (); controller.enter.connect (() => focused_visible = false); this.content.add_controller (controller); this.bind_property ("focus_visible", this, "focused_visible"); this.bind_property ("focus_widget", this, "focused_widget"); } private void button_command (SimpleAction action, Variant? parameter) { var name = parameter?.get_string (); if (name != null) { _store_panel.current_list.button_command ((!)name); } } private void remove_from_list (SimpleAction action, Variant? parameter) { var app = (Application) application; var uri = parameter?.get_string (); var music = uri != null ? app.loader.find_cache ((!)uri) : null; if (music != null) { _store_panel.remove_from_list ((!)music); } } private void save_list () { _store_panel.save_if_modified (false); } private void search_by (SimpleAction action, Variant? parameter) { var strv = parameter?.get_strv (); if (strv != null && ((!)strv).length > 1) { var arr = (!)strv; var text = arr[0] + ":"; var mode = SearchMode.ANY; parse_search_mode (ref text, ref mode); start_search (arr[1], mode); } } private void start_select () { _store_panel.current_list.multi_selection = true; if (_leaflet.folded) { _leaflet.pop (); } } private void toggle_search () { if (_store_panel.toggle_search () && _leaflet.folded) { _leaflet.pop (); } } private void update_background () { var paintable = _cover_paintable; if ((_bkgnd_blur == BlurMode.ALWAYS && paintable != null) || (_bkgnd_blur == BlurMode.ART_ONLY && paintable is Gdk.Texture)) { _bkgnd_paintable.paintable = create_blur_paintable (this, (!)paintable, _blur_size, _blur_size * 0.2, 0.25); } else { _bkgnd_paintable.paintable = null; } } public static Window? get_default () { return (GLib.Application.get_default () as Application)?.active_window as Window; } } public Gtk.Button? find_button_by_action_name (Gtk.Widget widget, string action) { for (var child = widget.get_first_child (); child != null; child = child?.get_next_sibling ()) { if (!((!)child).is_drawable ()) { continue; } else if (child is Gtk.Button) { var button = (Gtk.Button) child; if (button.action_name == action) return button; } else { var button = find_button_by_action_name ((!)child, action); if (button != null) return button; } } return null; } public File[] get_dropped_files (Value value) { File[] files = {}; var type = value.type (); if (type == Type.STRING) { var text = value.get_string (); var list = text.split_set ("\n"); files = new File[list.length]; var index = 0; foreach (var path in list) { files[index++] = File.new_for_path (path); } } else if (type == typeof (Gdk.FileList)) { var list = ((Gdk.FileList) value).get_files (); files = new File[list.length ()]; var index = 0; foreach (var file in list) { files[index++] = file; } } return files; } } ================================================ FILE: src/utils/async-task.vala ================================================ namespace G4 { public class Event { private Cond _cond = Cond (); private Mutex _mutex = Mutex (); private bool _notified = false; public void notify () { _mutex.lock (); _notified = true; _cond.broadcast (); _mutex.unlock (); } public void reset () { _mutex.lock (); _notified = false; _mutex.unlock (); } public void wait () { _mutex.lock (); while (!_notified) _cond.wait (_mutex); _mutex.unlock (); } } public delegate V TaskFunc (); public delegate void VoidFunc (); private class Worker { private TaskFunc _task; private SourceFunc _callback; private V? _result = null; public Worker (TaskFunc task, SourceFunc callback) { _task = task; _callback = callback; } public V? result { get { return _result; } } private void run () { _result = _task (); Idle.add ((owned) _callback); } private static Once> multi_thread_pool; internal static unowned ThreadPool get_multi_thread_pool () { return multi_thread_pool.once(() => new_thread_pool (get_num_processors ())); } private static Once> single_thread_pool; internal static unowned ThreadPool get_single_thread_pool () { return single_thread_pool.once(() => new_thread_pool (1)); } private static ThreadPool new_thread_pool (uint num_threads) { try { return new ThreadPool.with_owned_data ((tdata) => tdata.run(), (int) num_threads, false); } catch (Error e) { critical ("Create %u threads pool failed: %s\n", num_threads, e.message); Process.abort (); } } } public async V run_async (TaskFunc task, bool front = false, bool in_single_pool = false) { var worker = new Worker (task, run_async.callback); try { unowned var pool = in_single_pool ? Worker.get_single_thread_pool () : Worker.get_multi_thread_pool (); pool.add (worker); if (front) { pool.move_to_front (worker); } yield; } catch (Error e) { } return worker.result; } public uint run_idle_once (owned VoidFunc func, int priority = Priority.DEFAULT_IDLE) { return Idle.add (() => { func (); return false; }, priority); } public uint run_timeout_once (uint interval, owned VoidFunc func) { return Timeout.add (interval, () => { func (); return false; }); } public async void run_void_async (VoidFunc task) { yield run_async (task); } } ================================================ FILE: src/utils/cover-cache.vala ================================================ namespace G4 { public class CoverCache : Object { private HashTable _cache = new HashTable (str_hash, str_equal); private const string ATTRIBUTES = FileAttribute.STANDARD_CONTENT_TYPE + "," + FileAttribute.STANDARD_NAME; public File? find (File? parent) { if (parent == null) return null; var dir = (!)parent; var uri = dir.get_uri (); string? child = null; lock (_cache) { child = _cache[uri]; if (child == null) { child = find_no_lock (dir); _cache[uri] = child ?? ""; } } if (child == null || ((!)child).length == 0) return (File?) null; return dir.get_child ((!)child); } public void put (File dir, string child) { var uri = dir.get_uri (); lock (_cache) { _cache[uri] = child; } } private static string? find_no_lock (File dir) { try { FileInfo? pi = null; var enumerator = dir.enumerate_children (ATTRIBUTES, FileQueryInfoFlags.NONE); while ((pi = enumerator.next_file ()) != null) { var info = (!)pi; unowned var ctype = info.get_content_type () ?? ""; unowned var name = info.get_name (); if (is_cover_file (ctype, name)) { // print ("Find external cover: %s\n", name); return name; } } } catch (Error e) { } return null; } } public bool is_cover_file (string content_type, string name) { return ContentType.is_mime_type (content_type, "image/*") && (name.ascii_ncasecmp ("Cover", 5) == 0 || name.ascii_ncasecmp ("Folder", 6) == 0 || name.ascii_ncasecmp ("AlbumArt", 8) == 0); } } ================================================ FILE: src/utils/data-io.vala ================================================ namespace G4 { // Use BIG_ENDIAN, same as DataInputStream's default byte order public class DataInputBytes : Object { private Bytes _bytes; private unowned uint8[] _data; private int _pos = 0; private int _length; public DataInputBytes (Bytes bytes) { _bytes = bytes; _data = bytes.get_data (); _length = bytes.length; } public inline uint8 read_byte () throws IOError { if (_pos + 1 > _length) throw new IOError.INVALID_ARGUMENT (@"Pos:$_pos+1>$_length"); return _data[_pos++]; } public inline uint16 read_uint16 () throws IOError { if (_pos + 2 > _length) throw new IOError.INVALID_ARGUMENT (@"Pos:$_pos+2>$_length"); return ((uint16) (_data[_pos++]) << 8) | _data[_pos++]; } public inline uint32 read_uint32 () throws IOError { if (_pos + 4 > _length) throw new IOError.INVALID_ARGUMENT (@"Pos:$_pos+4>$_length"); return ((uint32) (_data[_pos++]) << 24) | ((uint32) (_data[_pos++]) << 16) | ((uint32) (_data[_pos++]) << 8) | _data[_pos++]; } public inline uint64 read_uint64 () throws IOError { uint64 hi = read_uint32 (); uint64 lo = read_uint32 (); return (hi << 32) | lo; } public size_t read_size () throws IOError { var n = read_byte (); switch (n) { case 254: return read_uint16 (); case 255: return read_uint32 (); default: return n; } } public string read_string () throws IOError { var size = (int) read_size (); if (size < 0 || _pos + size < 0 || _pos + size > _length) { throw new IOError.INVALID_ARGUMENT (@"Size:$_pos+$size>$_length"); } else if (size > 0) { var value = strndup ((char*) _data + _pos, size); _pos += size; return value; } return ""; } public void reset () { _pos = 0; } } // Use BIG_ENDIAN, same as DataOutputStream's default byte order public class DataOutputBytes : Object { private ByteArray _bytes; private uint8[] _data = new uint8[4]; public DataOutputBytes (uint reserved_size = 4096) { _bytes = new ByteArray.sized (reserved_size); } public inline void write_byte (uint8 n) { _data[0] = n; _bytes.append (_data[0:1]); } public inline void write_uint16 (uint16 n) { _data[0] = (uint8) (n >> 8); _data[1] = (uint8) (n); _bytes.append (_data[0:2]); } public inline void write_uint32 (uint32 n) { _data[0] = (uint8) (n >> 24); _data[1] = (uint8) (n >> 16); _data[2] = (uint8) (n >> 8); _data[3] = (uint8) (n); _bytes.append (_data[0:4]); } public inline void write_uint64 (uint64 n) { var hi = (uint32) (n >> 32); var lo = (uint32) (n); write_uint32 (hi); write_uint32 (lo); } public void write_size (size_t n) { if (n < 254) { write_byte ((uint8) n); } else if (n <= 0xffff) { write_byte (254); write_uint16 ((uint16) n); } else { write_byte (255); write_uint32 ((uint32) n); } } public void write_string (string str) { size_t size = str.length; write_size (size); if (size > 0) { unowned uint8[] data = (uint8[])str; _bytes.append (data[0:size]); } } public bool write_to (OutputStream stream) throws IOError { size_t bytes_written = 0; return stream.write_all (_bytes.data, out bytes_written); } } } ================================================ FILE: src/utils/dir-cache.vala ================================================ namespace G4 { namespace ChildType { public const uint8 NONE = 0; public const uint8 FOLDER = 1; public const uint8 MUSIC = 2; public const uint8 PLAYLIST = 3; public const uint8 COVER = 4; } public class DirCache : Object { private static uint32 MAGIC = 0x44495231; // 'DIR1' private class ChildInfo { public uint8 type; public string name; public int64 time; public ChildInfo (uint8 type, string name, int64 time) { this.type = type; this.name = name; this.time = time; } } private File _dir; private File _file; private int64 _time; private GenericArray _children = new GenericArray (128); public DirCache (File dir, FileInfo? info = null) { _dir = dir; var cache_dir = Environment.get_user_cache_dir (); var name = Checksum.compute_for_string (ChecksumType.MD5, dir.get_uri ()); _file = File.new_build_filename (cache_dir, Config.APP_ID, "dir-cache", name); _time = info?.get_modification_date_time ()?.to_unix () ?? 0; } public File dir { get { return _dir; } } public bool check_valid () { try { if (_time == 0) { var info = _dir.query_info (FileAttribute.TIME_MODIFIED, FileQueryInfoFlags.NONE); _time = info.get_modification_date_time ()?.to_unix () ?? 0; } var info = _file.query_info (FileAttribute.TIME_MODIFIED, FileQueryInfoFlags.NONE); return _time <= (info.get_modification_date_time ()?.to_unix () ?? 0); } catch (Error e) { } return false; } public void delete () { _file.delete_async.begin (Priority.DEFAULT, null, (obj, res) => { try { _file.delete_async.end (res); } catch (Error e) { } }); } public void add_child (FileInfo info, uint8 type) { var time = info.get_modification_date_time ()?.to_unix () ?? 0; var child = new ChildInfo (type, info.get_name (), time); _children.add (child); } public bool load (Queue stack, GenericArray musics, GenericArray? playlists, out string? cover_name) { cover_name = null; try { var mapped = new MappedFile (_file.get_path () ?? "", false); var dis = new DataInputBytes (mapped.get_bytes ()); var magic = dis.read_uint32 (); if (magic != MAGIC) throw new IOError.INVALID_DATA (@"Magic:$magic"); var base_name = _dir.get_basename () ?? ""; var bname = dis.read_string (); if (bname != base_name) throw new IOError.INVALID_DATA (@"Name:$bname!=$base_name"); var count = dis.read_size (); for (var i = 0; i < count; i++) { var type = dis.read_byte (); var name = dis.read_string (); var time = (int64) dis.read_uint64 (); var child = _dir.get_child (name); if (type == ChildType.MUSIC) { musics.add (new Music (child.get_uri (), name, time)); } else if (type == ChildType.FOLDER) { stack.push_head (new DirCache (child)); } else if (type == ChildType.PLAYLIST) { playlists?.add (child); } else if (type == ChildType.COVER) { cover_name = name; } } return true; } catch (Error e) { if (e.code != FileError.NOENT) print ("Load dir error: %s\n", e.message); } return false; } public void save () { try { var parent = _file.get_parent (); var exists = parent?.query_exists () ?? false; if (!exists) parent?.make_directory_with_parents (); var fos = _file.replace (null, false, FileCreateFlags.NONE); var dos = new DataOutputBytes (); dos.write_uint32 (MAGIC); dos.write_string (_dir.get_basename () ?? ""); dos.write_size (_children.length); foreach (var child in _children) { dos.write_byte (child.type); dos.write_string (child.name); dos.write_uint64 (child.time); } dos.write_to (fos); } catch (Error e) { print ("Save dir error: %s\n", e.message); } } } } ================================================ FILE: src/utils/dir-monitor.vala ================================================ namespace G4 { public class DirMonitor : Object { private HashTable _monitors = new HashTable (str_hash, str_equal); public signal void add_file (File file); public signal void remove_file (File file); ~DirMonitor () { remove_all (); } public bool enabled { get; set; } public void monitor (GenericArray dirs) { foreach (var dir in dirs) { if (dir.is_native ()) monitor_one (dir); } } public void monitor_one (File dir) { var uri = dir.get_uri (); unowned string orig_key; FileMonitor monitor; lock (_monitors) { if (_monitors.lookup_extended (uri, out orig_key, out monitor)) { monitor.cancel (); } } if (_enabled) try { monitor = dir.monitor (FileMonitorFlags.WATCH_MOVES, null); monitor.changed.connect (monitor_func); lock (_monitors) { _monitors[uri] = monitor; } } catch (Error e) { print ("Monitor dir error: %s\n", e.message); } } public void remove_all () { lock (_monitors) { _monitors.foreach ((uri, monitor) => monitor.cancel ()); _monitors.remove_all (); } } private void monitor_func (File file, File? other_file, FileMonitorEvent event) { switch (event) { case FileMonitorEvent.CHANGED: remove_file (file); add_file (file); break; case FileMonitorEvent.MOVED_IN: add_file (file); break; case FileMonitorEvent.DELETED: case FileMonitorEvent.MOVED_OUT: remove_file (file); break; case FileMonitorEvent.RENAMED: remove_file (file); if (other_file != null) add_file ((!)other_file); break; default: break; } } } } ================================================ FILE: src/utils/mpris.vala ================================================ namespace G4 { [DBus (name = "org.mpris.MediaPlayer2.Player")] public class MprisPlayer : Object { [DBus (visible = false)] private unowned Application _app; private unowned DBusConnection _connection; private bool _cover_parsed = false; private int64 _current_duration = 0; private unowned Music? _current_music = null; private HashTable _metadata = new HashTable (str_hash, str_equal); public MprisPlayer (Application app, DBusConnection connection) { _app = app; _connection = connection; app.index_changed.connect (on_index_changed); app.music_changed.connect (on_music_changed); app.music_cover_parsed.connect (on_music_cover_parsed); app.player.state_changed.connect (on_state_changed); app.player.duration_changed.connect (on_duration_changed); } public bool can_control { get { return true; } } public bool can_go_next { get { return _app.current_item < (int) _app.current_list.get_n_items () - 1; } } public bool can_go_previous { get { return _app.current_item > 0; } } public bool can_play { get { return _app.current_music != null; } } public bool can_pause { get { return _app.current_music != null; } } public bool can_seek { get { return _app.current_music != null; } } public HashTable metadata { get { return _metadata; } } public int64 position { get { return (int64) _app.player.position / Gst.USECOND; } } public string playback_status { owned get { return get_mpris_status(_app.player.state); } } public bool shuffle { get { return _app.sort_mode == SortMode.SHUFFLE; } set { if (value && (_app.sort_mode != SortMode.SHUFFLE)) { _app.sort_mode = SortMode.SHUFFLE; } else { _app.sort_mode = SortMode.TITLE; } } } public double volume { get { return _app.player.volume; } set { _app.player.volume = value; } } public void next () throws Error { _app.play_next (); } public void previous () throws Error { _app.play_previous (); } public void play_pause () throws Error { _app.play_pause(); } public void play () throws Error { _app.player.play (); } public void pause () throws Error { _app.player.pause (); } public void seek (int64 offset) throws Error { _app.player.position += offset * Gst.USECOND; } private void on_duration_changed (Gst.ClockTime duration) { var ms = (int64) duration / Gst.USECOND; if (_current_duration != ms) { _metadata.insert ("mpris:length", new Variant.int64 (ms)); if (_cover_parsed) send_property ("Metadata", _metadata); _current_duration = ms; } } private void on_index_changed (int index, uint size) { var builder = new VariantBuilder (new VariantType ("a{sv}")); builder.add ("{sv}", "CanGoNext", new Variant.boolean (index < (int) size - 1)); builder.add ("{sv}", "CanGoPrevious", new Variant.boolean (index > 0)); builder.add ("{sv}", "CanPlay", new Variant.boolean (_app.current_music != null)); builder.add ("{sv}", "CanPause", new Variant.boolean (_app.current_music != null)); send_properties (builder); } private void on_music_changed (Music? music) { _cover_parsed = false; _current_music = music; _current_duration = 0; _metadata.remove_all (); if (music != null) { var artists = new VariantBuilder (new VariantType ("as")); artists.add ("s", music?.artist ?? ""); _metadata.insert ("xesam:artist", artists.end()); _metadata.insert ("xesam:title", new Variant.string (music?.title ?? "")); _metadata.insert ("xesam:album", new Variant.string (music?.album ?? "")); _metadata.insert ("mpris:length", new Variant.int64 (_current_duration)); } } private void on_music_cover_parsed (Music music, Gdk.Pixbuf? pixbuf, string? uri) { if (_current_music != music) { on_music_changed (music); } if (uri != null) { _metadata.insert ("mpris:artUrl", new Variant.string ((!)uri)); } else { _metadata.remove ("mpris:artUrl"); } send_property ("Metadata", _metadata); _cover_parsed = true; } private void on_state_changed (Gst.State state) { send_property ("PlaybackStatus", new Variant.string (get_mpris_status(state))); } private void send_property (string name, Variant variant) { var builder = new VariantBuilder (new VariantType ("a{sv}")); builder.add ("{sv}", name, variant); send_properties (builder); } private void send_properties (VariantBuilder builder) { var invalid = new VariantBuilder (new VariantType ("as")); try { _connection.emit_signal ( null, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged", new Variant ( "(sa{sv}as)", "org.mpris.MediaPlayer2.Player", builder, invalid ) ); } catch (Error e) { warning ("Send MPRIS failed: %s\n", e.message); } } private string get_mpris_status (Gst.State state) { switch (state) { case Gst.State.PLAYING: return "Playing"; case Gst.State.PAUSED: return "Paused"; default: return "Stopped"; } } } [DBus (name = "org.mpris.MediaPlayer2")] public class MprisRoot : Object { private unowned Application _app; public MprisRoot (Application app) { _app = app; } public bool can_quit { get { return true; } } public bool can_raise { get { return true; } } public bool has_track_list { get { return false; } } public string desktop_entry { get { return _app.application_id; } } public string identity { get { return _app.name; } } public string[] supported_uri_schemes { owned get { return {"file", "smb"}; } } public string[] supported_mime_types { owned get { return {"audio/*"}; } } public void quit () throws Error { _app.quit (); } public void raise () throws Error { _app.activate (); } } } ================================================ FILE: src/utils/music-library.vala ================================================ namespace G4 { namespace SortMode { public const uint ALBUM = 0; public const uint ARTIST = 1; public const uint ARTIST_ALBUM = 2; public const uint TITLE = 3; public const uint RECENT = 4; public const uint SHUFFLE = 5; public const uint MAX = 5; } public class Album : Music { protected HashTable _musics = new HashTable (str_hash, str_equal); public Album (Music music) { base.titled (music.album, music.uri); base.album = music.album; base.artist = music.artist; base._album_key = music._album_key; base._artist_key = music._artist_key; base.date = music.date; base.track = music.track; base.uri = music.uri; } public uint length { get { return _musics.length; } } public bool add_music (Music music) { if (music.has_cover && music.track < track) { // For cover has_cover = true; uri = music.uri; } #if VALA_56_10 return _musics.insert (music.uri, music); #else return insert_music (music); #endif } protected bool insert_music (Music music) { var count = _musics.length; _musics.insert (music.uri, music); return _musics.length > count; } public bool contains (string uri) { return _musics.contains (uri); } public uint foreach_remove (HRFunc func) { return _musics.foreach_remove (func); } public virtual void get_sorted_musics (GenericArray musics) { _musics.foreach ((name, music) => musics.add (music)); sort (musics); } public void overwrite_to (ListStore store) { var musics = new GenericArray (_musics.length); get_sorted_musics (musics); store.splice (0, store.get_n_items (), (Object[]) musics.data); } public bool remove_music (Music music) { return _musics.remove (music.uri); } protected virtual void sort (GenericArray arr) { arr.sort (Music.compare_by_album); } } public class Artist : Music { protected HashTable _albums = new HashTable (str_hash, str_equal); public Artist (Music music, string artist_name) { base.titled (artist_name, music.uri); base.artist = artist_name; base.album_artist = music.album_artist; base._artist_key = artist_name.collate_key_for_filename (); base.date = music.date; base.uri = music.uri; } public uint length { get { return _albums.length; } } public override string get_abbreviation () { return parse_abbreviation (artist); } public bool add_music (Music music) { unowned string key; unowned var album_key = music.album_key; Album album; if (!_albums.lookup_extended (album_key, out key, out album)) { album = new Album (music); album.album_artist = artist; _albums[album_key] = album; } var added = album.add_music (music); if (added && album.has_cover && !has_cover) { has_cover = true; uri = album.uri; } return added; } public Album? find_by_partial_artist (string artist) { return _albums.find ((name, album) => artist.match_string (album.artist, true)) as Album; } public new Album? @get (string name) { return _albums[name]; } public void get_sorted_albums (GenericArray albums) { _albums.foreach ((name, album) => albums.add (album)); albums.sort (compare_album); } public void get_sorted_musics (GenericArray musics) { var arr = new GenericArray (_albums.length); get_sorted_albums (arr); arr.foreach ((album) => album.get_sorted_musics (musics)); } public void overwrite_store (ListStore store) { var arr = new GenericArray (_albums.length); get_sorted_albums (arr); store.splice (0, store.get_n_items (), (Object[]) arr.data); } public bool remove_music (Music music) { unowned var album_key = music.album_key; var album = _albums[album_key]; if (album is Album) { var ret = album.remove_music (music); if (album.length == 0) _albums.remove (album_key); return ret; } return _albums.foreach_remove ((name, album) => album.remove_music (music) && album.length == 0) > 0; } public Playlist to_playlist () { var playlist = new Playlist (title); get_sorted_musics (playlist.items); playlist.set_cover_uri (); return playlist; } private static int compare_album (Music m1, Music m2) { return (m1.date > 0 && m2.date > 0) ? (int) (m1.date - m2.date) : strcmp (m1._album_key, m2._album_key); } } public class Playlist : Album { public GenericArray items = new GenericArray (128); public string list_uri; public Playlist (string name, string uri = "") { base.titled (name, ""); base.album = name; set_list_uri (uri); } public new uint length { get { return items.length; } } public new bool add_music (Music music) { #if VALA_56_10 if (_musics.insert (music.uri, music)) { #else if (insert_music (music)) { #endif var count = items.length; items.add (music); items[count]._order = count; return true; } return false; } public void clear () { _musics.remove_all (); items.remove_range (0, items.length); } public void copy_from (Playlist playlist) { clear (); extend (playlist.items); set_cover_uri (); set_title (playlist.title); } public void extend (GenericArray musics) { items.extend (musics, (src) => src); } public new bool remove_music (Music music) { base.remove_music (music); return items.remove (music); } public override void get_sorted_musics (GenericArray musics) { musics.extend (items, (src) => src); } public void insert_to_store (ListStore store, uint position = uint.MAX) { var size = store.get_n_items (); store.splice (uint.min (position, size), 0, (Object[]) items.data); } public void reset_original_order () { Music.original_order (items); } public void set_cover_uri () { has_cover = false; foreach (var music in items) { if (music.has_cover) { has_cover = true; uri = music.uri; break; } } if (!has_cover && items.length > 0) { has_cover = true; uri = items[0].uri; } } public void set_list_uri (string uri) { _album_key = uri; list_uri = uri; } public void set_title (string title) { this.album = title; this.title = title; _title_key = title.collate_key_for_filename (); } protected override void sort (GenericArray arr) { Music.original_order (items); arr.sort (Music.compare_by_order); } } public class MusicLibrary : Object { private HashTable _albums = new HashTable (str_hash, str_equal); private HashTable _artists = new HashTable (str_hash, str_equal); private HashTable _playlists = new HashTable (str_hash, str_equal); public bool empty { get { return _albums.length == 0 && _artists.length == 0 && _playlists.length == 0; } } public uint album_count { get { return _albums.length; } } public uint artist_count { get { return _artists.length; } } public uint playlist_count { get { return _playlists.length; } } public bool add_music (Music music) { unowned string key; unowned var album_key = music.album_key; Album album; lock (_albums) { if (!_albums.lookup_extended (album_key, out key, out album)) { album = new Album (music); album.album_artist = ""; _albums[album_key] = album; } } var added = album.add_music (music); unowned var artist_name = music.artist_name; Artist artist; lock (_artists) { if (!_artists.lookup_extended (artist_name, out key, out artist)) { artist = new Artist (music, artist_name); _artists[artist_name] = artist; } } added |= artist.add_music (music); return added; } public Playlist add_playlist (Playlist playlist) { unowned string key; Playlist oldlist; lock (_playlists) { if (_playlists.lookup_extended (playlist.list_uri, out key, out oldlist)) { if (oldlist != playlist) oldlist.copy_from (playlist); return oldlist; } else { _playlists.insert (playlist.list_uri, playlist); } } return playlist; } public Album? get_album (string key) { lock (_albums) { return _albums[key]; } } public Artist? get_artist (string key) { lock (_artists) { return _artists[key]; } } public Playlist? get_playlist (string key) { lock (_playlists) { return _playlists[key]; } } public void overwrite_albums_to (ListStore store) { var arr = new GenericArray (_albums.length); lock (_albums) { _albums.foreach ((name, album) => arr.add (album)); } arr.sort (Music.compare_by_album); store.splice (0, store.get_n_items (), (Object[]) arr.data); } public void overwrite_artists_to (ListStore store) { var arr = new GenericArray (_artists.length); lock (_artists) { _artists.foreach ((name, artist) => arr.add (artist)); } arr.sort (Music.compare_by_artist); store.splice (0, store.get_n_items (), (Object[]) arr.data); } public void overwrite_playlists_to (ListStore store) { var arr = new GenericArray (_playlists.length); lock (_playlists) { _playlists.foreach ((uri, playlist) => arr.add (playlist)); } arr.sort (Music.compare_by_title); store.splice (0, store.get_n_items (), (Object[]) arr.data); } public void remove_music (Music music) { unowned var album_key = music.album_key; lock (_albums) { var album = _albums[album_key]; if (album is Album) { album.remove_music (music); if (album.length == 0) _albums.remove (album_key); } else { _albums.foreach_remove ((name, album) => album.remove_music (music) && album.length == 0); } } unowned var artist_name = music.artist_name; lock (_artists) { var artist = _artists[artist_name]; if (artist is Artist) { artist.remove_music (music); if (artist.length == 0) _artists.remove (artist_name); } else { _artists.foreach_remove ((name, artist) => artist.remove_music (music) && artist.length == 0); } } } public bool remove_playlist (string key) { lock (_playlists) { return _playlists.remove (key); } } public bool remove_uri (string uri, GenericSet removed) { string key; Playlist oldlist; lock (_playlists) { if (_playlists.steal_extended (uri, out key, out oldlist)) { oldlist.clear (); return true; } } var prefix = uri + "/"; uint n_removed = 0; lock (_albums) { n_removed = _albums.foreach_remove ((name, album) => { album.foreach_remove ((uri, music) => { unowned var uri2 = music.uri; if (uri2.has_prefix (prefix)/*|| uri2 == uri*/) { removed.add (music); return true; } return false; }); return album.length == 0; }); } removed.foreach ((music) => { lock (_artists) { _artists.foreach_remove ((name, artist) => artist.remove_music (music) && artist.length == 0); } }); return n_removed != 0; } public void remove_all () { lock (_albums) { _albums.remove_all (); } lock (_artists) { _artists.remove_all (); } lock (_playlists) { _playlists.remove_all (); } } } public int find_item_in_model (ListModel model, Object? obj, int start_pos = 0) { var count = model.get_n_items (); for (var i = start_pos; i < count; i++) { if (model.get_item (i) == obj) return (int) i; } for (var i = 0; i < start_pos; i++) { if (model.get_item (i) == obj) return (int) i; } return -1; } public bool merge_items_to_store (ListStore store, GenericArray arr, ref uint position) { var first_pos = -1; var removed = remove_items_from_store (store, arr, out first_pos); if (position >= first_pos + removed) position -= removed; position = uint.min (position, store.get_n_items ()); store.splice (position, 0, (Object[]) arr.data); return !(arr.length == 1 && arr[0] == store.get_item (first_pos)); } public int remove_items_from_store (ListStore store, GenericArray arr, out int first_pos = null) { var map = new GenericSet (direct_hash, direct_equal); arr.foreach ((obj) => map.add (obj)); var removed = 0; var first_removed = -1; var size = (int) store.get_n_items (); if (arr.length < size / 4) { for (var i = size - 1; i >= 0; i--) { var obj = store.get_item (i); if (map.contains (obj)) { store.remove (i); removed++; first_removed = i; } } } else { var remain = new GenericArray (size); for (var i = 0; i < size; i++) { var obj = store.get_item (i); if (!map.contains (obj)) { remain.add ((Music) obj); } else { removed++; if (first_removed == -1) first_removed = i; } } store.splice (0, size, (Object[]) remain.data); } first_pos = first_removed; return removed; } public void sort_music_array (GenericArray arr, uint sort_mode) { if (sort_mode == SortMode.SHUFFLE) Music.shuffle_order (arr); CompareFunc compare = Music.compare_by_order; switch (sort_mode) { case SortMode.ALBUM: compare = Music.compare_by_album; break; case SortMode.ARTIST: compare = Music.compare_by_artist; break; case SortMode.ARTIST_ALBUM: compare = Music.compare_by_artist_album; break; case SortMode.TITLE: compare = Music.compare_by_title; break; case SortMode.RECENT: compare = Music.compare_by_recent; break; } arr.sort (compare); } public void sort_music_store (ListStore store, uint sort_mode) { var count = store.get_n_items (); var arr = new GenericArray (count); for (var pos = 0; pos < count; pos++) { arr.add ((Music) store.get_item (pos)); } sort_music_array (arr, sort_mode); store.splice (0, count, (Object[]) arr.data); } public Playlist to_playlist (Music[] musics, string? title = null) { var count = musics.length; var arr = new GenericArray (count); foreach (var music in musics) { if (music is Artist) { ((Artist) music).get_sorted_musics (arr); } else if (music is Album) { ((Album) music).get_sorted_musics (arr); } else { arr.add (music); } } if (title == null && count == 1) { title = musics[0].title; } var playlist = new Playlist (title ?? _("Untitled")); arr.foreach ((music) => playlist.add_music (music)); return playlist; } } ================================================ FILE: src/utils/music-loader.vala ================================================ namespace G4 { public class Progress { private int _progress = 0; private int _total = 0; public Progress (int total = 0) { _total = total; } public int total { get { return AtomicInt.get (ref _total); } set { AtomicInt.set (ref _total, value); } } public double fraction { get { return _total > 0 ? _progress / (double) _total : 0; } } public void reset () { _progress = 0; _total = 0; } public void step () { AtomicInt.inc (ref _progress); } } public class StopWatch { private int64 _start_time; public StopWatch () { _start_time = get_monotonic_time (); } public int64 lap () { var now = get_monotonic_time (); var used = now - _start_time; _start_time = now; return used; } } public class MusicLoader : Object { private static Once?> _save_dir_pool; static unowned ThreadPool? get_save_dir_pool () { return _save_dir_pool.once(() => { try { return new ThreadPool.with_owned_data ((cache) => cache.save (), 1, false); } catch (Error e) { } return null; }); } private CoverCache _cover_cache = new CoverCache (); private DirMonitor _dir_monitor = new DirMonitor (); private MusicLibrary _library = new MusicLibrary (); private Progress _progress = new Progress (); private TagCache _tag_cache = new TagCache (); public signal void loading_changed (bool loading); public signal void music_found (GenericArray arr); public signal void music_lost (GenericSet arr); public MusicLoader () { _dir_monitor.add_file.connect (on_file_added); _dir_monitor.remove_file.connect (on_file_removed); } public CoverCache cover_cache { get { return _cover_cache; } } public bool monitor_changes { get { return _dir_monitor.enabled; } set { _dir_monitor.enabled = value; } } public double loading_progress { get { return _progress.fraction; } } public MusicLibrary library { get { return _library; } } public void add_to_cache (Music music) { _tag_cache.add (music); } public Music? find_cache (string uri) { return _tag_cache[uri]; } public void load_tag_cache () { run_void_async.begin (_tag_cache.load, (obj, res) => run_void_async.end (res)); } public void save_tag_cache () { if (_tag_cache.modified) { run_void_async.begin (_tag_cache.save, (obj, res) => run_void_async.end (res)); } } public async void load_files_async (owned File[] files, GenericArray musics, bool ignore_exists = false, bool merge_lists = true, uint sort_mode = -1) { var dirs = new GenericArray (128); _progress.reset (); loading_changed (true); yield run_void_async (() => { var list_files = new GenericArray (128); var stop_watch = new StopWatch (); foreach (var file in files) { add_file (file, musics, dirs, list_files); } print ("Find %u files in %d folders in %lld ms\n", musics.length, dirs.length, stop_watch.lap () / 1000); var playlists = new GenericArray (list_files.length); load_playlists (musics, list_files, playlists, merge_lists); load_tags_in_threads (musics); print ("Load %u musics in %lld ms\n", musics.length, stop_watch.lap () / 1000); add_musics_to_library (musics, playlists, ignore_exists); if (sort_mode <= SortMode.MAX) { sort_music_array (musics, sort_mode); } print ("Group %u artists %u albums %u playlists in %lld ms\n", _library.artist_count, _library.album_count, _library.playlist_count, stop_watch.lap () / 1000); }); loading_changed (false); run_void_async.begin (() => _dir_monitor.monitor (dirs), (obj, res) => run_void_async.end (res)); save_tag_cache (); } public void remove_all () { _dir_monitor.remove_all (); _library.remove_all (); } private const string ATTRIBUTES = FileAttribute.STANDARD_CONTENT_TYPE + "," + FileAttribute.STANDARD_IS_HIDDEN + "," + FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE + "," + FileAttribute.TIME_MODIFIED; private void add_file (File file, GenericArray musics, GenericArray dirs, GenericArray playlists) { try { var info = file.query_info (ATTRIBUTES, FileQueryInfoFlags.NONE); if (info.get_file_type () == FileType.DIRECTORY) { var stack = new Queue (); stack.push_head (new DirCache (file, info)); while (stack.length > 0) { var cache = stack.pop_head (); dirs.add (cache.dir); add_directory (cache, stack, musics, playlists); } } else { unowned var ctype = info.get_content_type () ?? ""; unowned var name = info.get_name (); if (is_music_type (ctype)) { var time = info.get_modification_date_time ()?.to_unix () ?? 0; var music = new Music (file.get_uri (), name, time); musics.add (music); } else if (is_playlist_file (ctype)) { playlists.add (file); } else if (is_cover_file (ctype, name)) { var parent = file.get_parent (); if (parent != null) _cover_cache.put ((!)parent, name); } else { print ("unknown type: %s, %s\n", ctype, file.get_path () ?? ""); } } } catch (Error e) { if (e.code != IOError.NOT_FOUND) print ("Query %s: %s\n", file.get_parse_name (), e.message); } } private void add_directory (DirCache cache, Queue stack, GenericArray musics, GenericArray playlists) { var dir = cache.dir; var start = musics.length; string? cover_name = null; if (cache.check_valid () && cache.load (stack, musics, playlists, out cover_name)) { _cover_cache.put (dir, cover_name ?? ""); } else try { FileInfo? pi = null; var enumerator = dir.enumerate_children (ATTRIBUTES, FileQueryInfoFlags.NONE); while ((pi = enumerator.next_file ()) != null) { var info = (!)pi; if (info.get_is_hidden ()) { continue; } else if (info.get_file_type () == FileType.DIRECTORY) { var child = dir.get_child (info.get_name ()); stack.push_head (new DirCache (child, info)); cache.add_child (info, ChildType.FOLDER); } else { unowned var ctype = info.get_content_type () ?? ""; unowned var name = info.get_name (); if (is_music_type (ctype)) { var time = info.get_modification_date_time ()?.to_unix () ?? 0; var file = dir.get_child (name); var music = new Music (file.get_uri (), name, time); musics.add (music); cache.add_child (info, ChildType.MUSIC); } else if (is_playlist_file (ctype)) { var child = dir.get_child (info.get_name ()); playlists.add (child); cache.add_child (info, ChildType.PLAYLIST); } else if (cover_name == null && is_cover_file (ctype, name)) { cover_name = name; cache.add_child (info, ChildType.COVER); } } } _cover_cache.put (dir, cover_name ?? ""); get_save_dir_pool ()?.add (cache); } catch (Error e) { if (e.code != IOError.NOT_FOUND) print ("Enumerate %s: %s\n", dir.get_parse_name (), e.message); } if (cover_name != null && ((!)cover_name).length > 0) { for (var i = musics.length - 1; i >= start; i--) { var music = (Music) musics[i]; music.has_cover = true; } } } private void add_music_file (File file, GenericArray musics) { try { var info = file.query_info (FileAttribute.TIME_MODIFIED, FileQueryInfoFlags.NONE); unowned var name = file.get_basename () ?? ""; var time = info.get_modification_date_time ()?.to_unix () ?? 0; var music = new Music (file.get_uri (), name, time); musics.add (music); } catch (Error e) { if (e.code != IOError.NOT_FOUND) print ("Query %s: %s\n", file.get_parse_name (), e.message); } } private void add_musics_to_library (GenericArray musics, GenericArray playlists, bool ignore_exists) { for (var i = musics.length - 1; i >= 0; i--) { var music = musics[i]; if (!_library.add_music (music) && ignore_exists) musics.remove_index_fast (i); } foreach (var playlist in playlists) { unowned var items = playlist.items; for (var i = items.length - 1; i >= 0; i--) { var music = items[i]; items[i] = _tag_cache[music.uri] ?? music; } playlist.set_cover_uri (); _library.add_playlist (playlist); } } private void load_playlists (GenericArray musics, GenericArray list_files, GenericArray playlists, bool merge_lists) { foreach (var file in list_files) { if (file.is_native ()) { var uris = new GenericArray (1024); var name = load_playlist_file (file, uris); if (name != null && uris.length > 0) { var playlist = new Playlist ((!)name, file.get_uri ()); uris.foreach ((uri) => add_music_file (File.new_for_uri (uri), playlist.items)); playlists.add (playlist); if (merge_lists) musics.extend (playlist.items, (src) => src); } } } } private void load_tags_in_threads (GenericArray musics) { var queue = new AsyncQueue (); _tag_cache.wait_loading (); for (var i = musics.length - 1; i >= 0; i--) { unowned var music = musics[i]; var cached_music = _tag_cache[music.uri]; if (cached_music != null && ((!)cached_music).modified_time == music.modified_time) { musics[i] = (!)cached_music; } else { _tag_cache.add (music); queue.push (music); } } var queue_count = queue.length (); if (queue_count > 0) { _progress.total = queue_count; var num_tasks = uint.min (queue_count, get_num_processors ()); run_in_threads (() => { Music? music; while ((music = queue.try_pop ()) != null) { music?.parse_tags (); _progress.step (); } }, num_tasks); } } public async void on_file_added (File file) { try { var info = yield file.query_info_async (FileAttribute.STANDARD_IS_HIDDEN, FileQueryInfoFlags.NONE); if (info.get_is_hidden ()) return; } catch (Error e) { return; } var arr = new GenericArray (1024); var n_playlists = (int) _library.playlist_count; yield load_files_async ({file}, arr, true, false, -1); n_playlists -= (int) _library.playlist_count; if (arr.length > 0 || n_playlists != 0) { music_found (arr); } } public async void on_file_removed (File file) { var result = false; var removed = new GenericSet (direct_hash, direct_equal); yield run_void_async (() => { var uri = file.get_uri (); var music = _tag_cache.remove (uri); if (music != null) { _library.remove_music ((!)music); removed.add ((!)music); } else { result = _library.remove_uri (uri, removed); new DirCache (file).delete (); } }); if (removed.length > 0 || result) { music_lost (removed); } } private delegate G ThreadFunc (); private static void run_in_threads (owned ThreadFunc func, uint num_tasks) { var threads = new Thread[num_tasks]; for (var i = 0; i < num_tasks; i++) { var index = i; threads[index] = new Thread (null, func); } foreach (var thread in threads) { thread.join (); } } } } ================================================ FILE: src/utils/music.vala ================================================ namespace G4 { public const string UNKNOWN_ALBUM = _("Unknown Album"); public const string UNKNOWN_ARTIST = _("Unknown Artist"); public class Music : Object { public string album = ""; public string artist = ""; public string title = ""; public string album_artist = ""; public uint32 date = 0; public string genre = ""; public int track = 0; public int disc = 0; public bool has_cover = false; public int64 modified_time = 0; public string uri = ""; // for runtime public string? cover_uri = null; // for sorting protected string _album_key = ""; protected string _artist_key = ""; protected string _title_key = ""; private string? _cover_key = null; protected int _order = 0; public Music (string uri, string title, int64 time) { this.title = title; this.uri = uri; this.modified_time = time; } public Music.empty () { } public Music.titled (string title, string uri) { this.title = title; this.uri = uri; _title_key = title.collate_key_for_filename (); } public unowned string album_key { get { return _album_key; } } public unowned string artist_name { get { return album_artist.length > 0 ? album_artist : artist; } } public unowned string cover_key { get { return _cover_key ?? uri; } set { _cover_key = value; } } public inline string get_artist_and_title () { return artist == UNKNOWN_ARTIST ? title : @"$artist - $title"; } public virtual string get_abbreviation () { return parse_abbreviation (album); } public inline string get_date_string () { var year = date / 400; if (year > 0) { var month = (date - year * 400) / 32; if (month > 0) { var d = date % 32; return d > 0 ? @"$year-$month-$d" : @"$year-$month"; } return year.to_string (); } return ""; } public bool from_gst_tags (Gst.TagList tags) { var changed = false; unowned string? al = null, ar = null, ti = null, aa = null, ge = null; if (tags.peek_string_index (Gst.Tags.ALBUM, 0, out al) && al != null && strcmp (album, al) != 0) { album = (!)al; changed = true; } if (tags.peek_string_index (Gst.Tags.ARTIST, 0, out ar) && ar != null && strcmp (artist, ar) != 0) { artist = (!)ar; _artist_key = artist.collate_key_for_filename (); changed = true; } if (tags.peek_string_index (Gst.Tags.TITLE, 0, out ti) && ti != null && strcmp (title, ti) != 0) { title = (!)ti; _title_key = title.collate_key_for_filename (); changed = true; } if (tags.peek_string_index (Gst.Tags.ALBUM_ARTIST, 0, out aa) && aa != null && strcmp (album_artist, aa) != 0) { album_artist = (!)aa; changed = true; } if (tags.peek_string_index (Gst.Tags.GENRE, 0, out ge) && ge != null && strcmp (genre, ge) != 0) { genre = (!)ge; changed = true; } Gst.DateTime? dt = null; uint dtu = 0; if (tags.get_date_time (Gst.Tags.DATE_TIME, out dt) && dt != null && (dtu = gst_date_time_to_uint (dt)) != date) { date = dtu; changed = true; } if ((dt = parse_gst_original_date (tags)) != null && dt != null && (dtu = gst_date_time_to_uint (dt)) != date) { date = dtu; changed = true; } uint tn = 0, avn = 0; if (tags.get_uint (Gst.Tags.TRACK_NUMBER, out tn) && (int) tn > 0 && track != tn) { track = (int) tn; changed = true; } if (tags.get_uint (Gst.Tags.ALBUM_VOLUME_NUMBER, out avn) && (int) avn > 0 && disc != avn) { disc = (int) avn; changed = true; } Gst.Sample? sample = null; if (tags.get_sample (Gst.Tags.IMAGE, out sample) && has_cover != (sample != null)) { has_cover = sample != null; changed = true; } if (changed) { update_album_key (); } return changed; } public bool has_unknown () { return title.length == 0 || artist == UNKNOWN_ARTIST || album == UNKNOWN_ALBUM; } public Music.deserialize (DataInputBytes dis) throws IOError { album = dis.read_string (); artist = dis.read_string (); title = dis.read_string (); has_cover = dis.read_byte () == 1; modified_time = (int64) dis.read_uint64 (); uri = dis.read_string (); album_artist = dis.read_string (); date = dis.read_uint32 (); genre = dis.read_string (); track = (int) dis.read_size (); disc = (int) dis.read_size (); update_album_key (); _artist_key = artist.collate_key_for_filename (); _title_key = title.collate_key_for_filename (); } public void serialize (DataOutputBytes dos) throws IOError { dos.write_string (album); dos.write_string (artist); dos.write_string (title); dos.write_byte (has_cover ? 1 : 0); dos.write_uint64 (modified_time); dos.write_string (uri); dos.write_string (album_artist); dos.write_uint32 (date); dos.write_string (genre); dos.write_size (track); dos.write_size (disc); } public void parse_tags () { var file = File.new_for_uri (uri); var name = title; this.title = ""; if (file.is_native ()) { var tags = parse_gst_tags (file); if (tags != null) from_gst_tags ((!)tags); } if (title.length == 0 || artist.length == 0) { // guess tags from the file name var end = name.last_index_of_char ('.'); if (end > 0) { name = name.substring (0, end); } int track_index = 0; var pos = name.index_of_char ('.'); if (pos > 0 && pos < name.length - 1) { // assume prefix number as track index if (int.try_parse (name.substring (0, pos), out track_index, null, 10)) name = name.substring (pos + 1); } // split the file name by '-' var sa = name.split ("-"); var len = sa.length; if (title.length == 0) { title = len >= 1 ? sa[len - 1].strip () : name; _title_key = title.collate_key_for_filename (); } if (artist.length == 0) { artist = len >= 2 ? sa[len - 2].strip () : UNKNOWN_ARTIST; _artist_key = artist.collate_key_for_filename (); } if (track_index == 0) { if (track_index == 0 && len >= 3) int.try_parse (sa[0].strip (), out track_index, null, 10); if (track_index > 0) this.track = track_index; } } if (album.length == 0) { // assume folder name as the album album = file.get_parent ()?.get_basename () ?? UNKNOWN_ALBUM; _album_key = album.collate_key_for_filename (); } } private void update_album_key () { _album_key = (album + date.to_string () + album_artist).collate_key_for_filename (); } public static int compare_by_album (Music s1, Music s2) { int ret = strcmp (s1._album_key, s2._album_key); if (ret != 0) return ret; ret = s1.disc - s2.disc; if (ret != 0) return ret; ret = s1.track - s2.track; if (ret != 0) return ret; ret = strcmp (s1._title_key, s2._title_key); if (ret != 0) return ret; return strcmp (s1.uri, s2.uri); } public static int compare_by_artist (Music s1, Music s2) { int ret = strcmp (s1._artist_key, s2._artist_key); if (ret != 0) return ret; ret = strcmp (s1._title_key, s2._title_key); if (ret != 0) return ret; return strcmp (s1.uri, s2.uri); } public static int compare_by_artist_album (Music s1, Music s2) { int ret = strcmp (s1._artist_key, s2._artist_key); if (ret != 0) return ret; return compare_by_album (s1, s2); } public static int compare_by_title (Music s1, Music s2) { int ret = strcmp (s1._title_key, s2._title_key); if (ret != 0) return ret; ret = strcmp (s1._artist_key, s2._artist_key); if (ret != 0) return ret; return strcmp (s1.uri, s2.uri); } public static int compare_by_order (Music s1, Music s2) { return s1._order - s2._order; } public static int compare_by_recent (Music s1, Music s2) { var diff = s2.modified_time - s1.modified_time; return (int) diff.clamp (-1, 1); } public static inline uint32 gst_date_time_to_uint (Gst.DateTime? dt) { if (dt != null) { var d = (!)dt; return (d.has_year () ? d.get_year () : 0) * 400 + (d.has_month () ? d.get_month () : 0) * 32 + (d.has_day () ? d.get_day () : 0); } return 0; } public static Gst.DateTime? parse_gst_original_date (Gst.TagList tags) { Gst.DateTime? date = null; Gst.DateTime? year = null; var ec_size = tags.get_tag_size (Gst.Tags.EXTENDED_COMMENT); for (int i = 0; i < ec_size; i++) { string? s = null; if (tags.peek_string_index (Gst.Tags.EXTENDED_COMMENT, i, out s) && s != null) { var str = (!)s; if (str.has_prefix ("ORIGINALDATE=")) date = new Gst.DateTime.from_iso8601_string (str.substring (13)); else if (str.has_prefix ("ORIGINALYEAR=")) year = new Gst.DateTime.from_iso8601_string (str.substring (13)); if (date != null && year != null) break; } } return date ?? year; } public static void original_order (GenericArray arr) { for (var i = arr.length - 1; i >= 0; i--) { arr[i]._order = i; } } public static void shuffle_order (GenericArray arr) { for (var i = arr.length - 1; i > 0; i--) { var r = Random.int_range (0, i + 1); var s = arr[i]; arr[i] = arr[r]; arr[r] = s; arr[i]._order = i; } } } public bool is_music_type (string content_type) { return ContentType.is_mime_type (content_type, "audio/*") && !content_type.has_suffix ("pls") && !content_type.has_suffix ("url"); } public unichar find_first_letter (string text) { var index = 0; var next = 0; unichar c = 0; while (text.get_next_char (ref next, out c)) { if (c.isalpha () || c.iswide_cjk ()) return c; index = next; } return 0; } public string get_display_name (string uri) { return get_file_display_name (File.new_for_uri (uri)); } public string get_file_display_name (File file) { var name = file.get_basename () ?? ""; if (name.length == 0 || name == "/") name = file.get_parse_name (); return name.substring (0, name.index_of_char ('.')); } public string get_uri_with_end_sep (File file) { var uri = file.get_uri (); if (uri[uri.length - 1] != '/') uri += "/"; return uri; } public string parse_abbreviation (string text) { var sb = new StringBuilder (); var char_count = 0; var arr = text.tokenize_and_fold ("", null); for (var i = 0; i < arr.length && char_count < 2; i++) { unowned var s = arr[i]; unichar c = 0; if (s.ascii_ncasecmp ("feat", 4) != 0 && (c = find_first_letter (s)) > 0) { sb.append_unichar (c); char_count++; if (char_count >= 2) break; } } if (char_count >= 2) { return sb.str.up (); } else if (text.char_count () > 2) { var index = text.index_of_nth_char (2); return text.substring (0, index).up (); } return text.up (); } } ================================================ FILE: src/utils/playlist-file.vala ================================================ namespace G4 { namespace PlayListType { public const uint NONE = 0; public const uint M3U = 1; public const uint PLS = 2; } public uint get_playlist_type (string mimetype) { switch (mimetype) { case "audio/x-mpegurl": case "public.m3u-playlist": return PlayListType.M3U; case "audio/x-scpls": case "public.pls-playlist": return PlayListType.PLS; default: return PlayListType.NONE; } } public bool is_playlist_file (string mimetype) { return get_playlist_type (mimetype) != PlayListType.NONE; } public bool is_valid_uri (string uri, UriFlags flags = UriFlags.NONE) { try { return Uri.is_valid (uri, flags); } catch (Error e) { } return false; } public string? load_playlist_file (File file, GenericArray uris) { string? title = null; try { var info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE, FileQueryInfoFlags.NONE); var type = get_playlist_type (info.get_content_type () ?? ""); var fis = file.read (); var bis = new BufferedInputStream (fis); var dis = new DataInputStream (bis); var parent = file.get_parent (); switch (type) { case PlayListType.M3U: title = load_m3u_file (dis, parent, uris); break; case PlayListType.PLS: title = load_pls_file (dis, parent, uris); break; } return title ?? get_file_display_name (file); } catch (Error e) { } return null; } public string? load_m3u_file (DataInputStream dis, File? parent, GenericArray uris) throws Error { size_t length = 0; string? str = null, title = null; while ((str = dis.read_line_utf8 (out length)) != null) { var line = (!)str; if (line.has_prefix ("#PLAYLIST:")) { var text = line.substring (10).strip (); if (text.length > 0) title = text; } else if (length > 0 && line[0] != '#') { var abs_uri = parse_relative_uri (line.strip (), parent); if (abs_uri != null) uris.add ((!)abs_uri); } } return title; } public string? load_pls_file (DataInputStream dis, File? parent, GenericArray uris) throws Error { bool list_found = false; size_t length = 0; string? str = null, title = null; while ((str = dis.read_line_utf8 (out length)) != null) { int pos = -1; var line = ((!)str).strip (); if (line.length > 1 && line[0] == '[') { list_found = line == "[playlist]"; } else if (list_found && (pos = line.index_of_char ('=')) > 0) { if (line.has_prefix ("File")) { var uri = line.substring (pos + 1).strip (); var abs_uri = parse_relative_uri (uri, parent); if (abs_uri != null) uris.add ((!)abs_uri); } else if (line.ascii_ncasecmp ("X-GNOME-Title", pos) == 0) { var text = line.substring (pos + 1).strip (); if (text.length > 0) title = text; } } } return title; } public string get_relative_path (File? parent, File file) { var path = parent?.get_relative_path (file); if (path != null) return (!)path; path = file.get_path (); if (path == null) return ""; var file_path = (!)path; path = parent?.get_path (); if (path == null) return file_path; var parent_path = (!)path; var pos = 0; do { var ret = file_path.index_of_char ('/', pos + 1); if (ret > pos && file_path.ascii_ncasecmp (parent_path, ret) == 0) pos = ret; else break; } while (pos > 0); if (pos > 1) { // Skip the first '/' var len = parent_path.length; var sb = new StringBuilder (); for (var p = pos; p > 0 && p < len; p = parent_path.index_of_char ('/', p + 1)) sb.append ("../"); file_path = sb.str + file_path.substring (pos + 1); } return file_path; } public string? parse_relative_uri (string uri, File? parent = null) { if (uri.length > 0 && uri[0] == '/') { return File.new_for_path (uri).get_uri (); } else if (is_valid_uri (uri)) { // Native files only return uri.has_prefix ("file://") ? (string?) uri : null; } return parent?.resolve_relative_path (uri)?.get_uri (); } public bool save_m3u8_file (DataOutputStream dos, File? parent, GenericArray uris, string? title, bool with_titles) throws Error { if (!dos.put_string ("#EXTM3U\n")) return false; if (title != null && with_titles && !dos.put_string (@"#PLAYLIST:$((!)title)\n")) return false; foreach (var uri in uris) { var f = File.new_for_uri (uri); var path = get_relative_path (parent, f); if (with_titles) { var name = get_file_display_name (f); if (!dos.put_string (@"#EXTINF:,$name\n")) return false; } if (!dos.put_string (@"$path\n")) return false; } return true; } public bool save_pls_file (DataOutputStream dos, File? parent, GenericArray uris, string? title, bool with_titles) throws Error { if (!dos.put_string ("[playlist]\n")) return false; if (title != null && with_titles && !dos.put_string (@"X-GNOME-Title=$((!)title)\n")) return false; var count = uris.length; if (!dos.put_string (@"NumberOfEntries=$count\n")) return false; for (var i = 0; i < count; i++) { var f = File.new_for_uri (uris[i]); var path = get_relative_path (parent, f); var n = i + 1; if (with_titles) { var name = get_file_display_name (f); if (!dos.put_string (@"Title$n=$name\n")) return false; } if (!dos.put_string (@"File$n=$path\n")) return false; } return true; } public bool save_playlist_file (File file, GenericArray uris, string? title = null, bool with_titles = true) { var bname = file.get_basename () ?? ""; var pos = bname.last_index_of_char ('.'); var name = bname.substring (0, pos); var ext = bname.substring (pos + 1); try { var fos = file.replace (null, false, FileCreateFlags.NONE); var bos = new BufferedOutputStream (fos); var dos = new DataOutputStream (bos); var parent = file.get_parent (); if (ext.ascii_ncasecmp ("pls", 3) == 0) { return save_pls_file (dos, parent, uris, title ?? name, with_titles); } else { return save_m3u8_file (dos, parent, uris, title ?? name, with_titles); } } catch (Error e) { print ("Save playlist %s: %s\n", file.get_parse_name (), e.message); } return false; } } ================================================ FILE: src/utils/portal.vala ================================================ namespace G4 { public class Portal { private static string PORTAL_NAME = "org.freedesktop.portal."; private static string BUS_NAME = PORTAL_NAME + "Desktop"; private static string OBJECT_PATH = "/org/freedesktop/portal/desktop"; private DBusConnection? _bus = null; private string _parent; public Portal (string? parent = null) { _parent = parent ?? ""; } public async bool open_directory_async (string uri) throws Error { var file = File.new_for_uri (uri); var options = make_options_builder (); var parameters = new Variant ("(sha{sv})", _parent, 0, options); var ret = yield call_with_file_async (file, Posix.O_CLOEXEC, PORTAL_NAME + "OpenURI", "OpenDirectory", parameters); return ret != null; } public async void request_background_async (string? reason) { try { var options = make_options_builder (); if (reason != null) { options.add ("{sv}", "reason", new Variant.string ((!)reason)); } options.add ("{sv}", "autostart", new Variant.boolean (false)); options.add ("{sv}", "dbus-activatable", new Variant.boolean (false)); var parameters = new Variant ("(sa{sv})", _parent, options); _bus = _bus ?? yield Bus.get (BusType.SESSION); yield ((!)_bus).call (BUS_NAME, OBJECT_PATH, PORTAL_NAME + "Background", "RequestBackground", parameters, null, DBusCallFlags.NONE, -1); } catch (Error e) { print ("Bus.call error: %s\n", e.message); } } public async bool trash_file_async (string uri) throws Error { var file = File.new_for_uri (uri); var parameters = new Variant ("(h)", 0); var ret = yield call_with_file_async (file, Posix.O_CLOEXEC | 010000000 /*O_PATH*/, PORTAL_NAME + "Trash", "TrashFile", parameters); uint val = 0; ret?.get ("(u)", out val); return val == 1; } private async Variant? call_with_file_async (File file, int flags, string interface_name, string method_name, Variant? parameters = null) throws Error { var fd = -1; try { var path = file.get_path () ?? file.get_parse_name (); fd = Posix.open (path, flags); if (fd == -1) throw IOError.from_errno (Posix.errno); var fd_list = new GLib.UnixFDList (); fd_list.append (fd); _bus = _bus ?? yield Bus.get (BusType.SESSION); return yield ((!)_bus).call_with_unix_fd_list (BUS_NAME, OBJECT_PATH, interface_name, method_name, parameters, null, DBusCallFlags.NONE, -1, fd_list); } catch (Error e) { if (fd != -1) { Posix.close (fd); } throw e; } } private VariantBuilder make_options_builder () { var token = "portal" + Random.next_int ().to_string (); var options = new VariantBuilder (VariantType.VARDICT); options.add ("{sv}", "handle_token", new Variant.string (token)); return options; } } } ================================================ FILE: src/utils/tag-cache.vala ================================================ namespace G4 { public class TagCache { private static uint32 MAGIC = 0x54414733; // 'TAG3' private Event _event = new Event (); private File _file; private bool _modified = false; private HashTable _cache = new HashTable (str_hash, str_equal); public TagCache (string name = "tag-cache") { var dir = Environment.get_user_cache_dir (); _file = File.new_build_filename (dir, Config.APP_ID, name); } public bool modified { get { return _modified; } } public Music? @get (string uri) { unowned string key; unowned Music music; lock (_cache) { if (_cache.lookup_extended (uri, out key, out music)) { return music; } } return null; } public void add (Music music) { lock (_cache) { _cache[music.uri] = music; _modified = true; } } public Music? remove (string uri) { string key; Music value; lock (_cache) { if (_cache.steal_extended (uri, out key, out value)) { _modified = true; return value; } } return null; } public void load () { _event.reset (); try { var mapped = new MappedFile (_file.get_path () ?? "", false); var dis = new DataInputBytes (mapped.get_bytes ()); var magic = dis.read_uint32 (); if (magic != MAGIC) throw new IOError.INVALID_DATA (@"Magic=$magic"); var count = dis.read_size (); lock (_cache) { for (var i = 0; i < count; i++) { var music = new Music.deserialize (dis); _cache[music.uri] = music; } } } catch (Error e) { if (e.code != FileError.NOENT) print ("Load tags error: %s\n", e.message); } _event.notify (); } public void save () { _event.reset (); try { var parent = _file.get_parent (); var exists = parent?.query_exists () ?? false; if (!exists) parent?.make_directory_with_parents (); var fos = _file.replace (null, false, FileCreateFlags.NONE); var dos = new DataOutputBytes (); dos.write_uint32 (MAGIC); lock (_cache) { dos.write_size (_cache.length); _cache.foreach ((key, music) => { try { music.serialize (dos); } catch (Error e) { } }); _modified = !dos.write_to (fos); } } catch (Error e) { print ("Save tags error: %s\n", e.message); } _event.notify (); } public void wait_loading () { _event.wait (); } } } ================================================ FILE: src/utils/thumbnailer.vala ================================================ namespace G4 { // Sorted by insert order public class LruCache : Object { private size_t _max_size = 0; private size_t _size = 0; private HashTable _cache = new HashTable (str_hash, str_equal); private Queue _queue = new Queue (); public LruCache (size_t max_size) { _max_size = max_size; } public V find (string key) { return _cache[key]; } public void put (string key, V value, bool replace = false) { if (replace) { remove (key); } else if (_cache.contains (key)) { return; } var size = size_of_value (value); while (_size + size > _max_size && _queue.length > 0) { string stolen_key; V stolen_value; if (_cache.steal_extended (_queue.pop_head (), out stolen_key, out stolen_value)) { _size -= size_of_value (stolen_value); } } _cache[key] = value; _queue.push_tail (key); _size += size; // print (@"Cache $(_cache.length)/$(_queue.length) items, $_size bytes\n"); } public bool remove (string key) { string stolen_key; V stolen_value; if (_cache.steal_extended (key, out stolen_key, out stolen_value)) { unowned var link = _queue.find_custom (key, strcmp); if (link != (List) null) _queue.unlink (link); _size -= size_of_value (stolen_value); return true; } return false; } public void remove_all () { _cache.remove_all (); _queue.clear (); _size = 0; } protected virtual size_t size_of_value (V value) { return 1; } } public class Thumbnailer : Object { public const int GRID_SIZE = 160; public const int ICON_SIZE = 48; private HashTable _album_covers = new HashTable (str_hash, str_equal); private LruCache _album_pixbufs = new LruCache (1000); private LruCache _grid_cache = new LruCache (500); private LruCache _icon_cache = new LruCache (1000); private Quark _loading_quark = Quark.from_string ("loading_quark"); public signal void tag_updated (Music music); public CoverCache? cover_finder { get; set; } public Pango.Context? pango_context { get; set; } public bool remote_thumbnail { get; set; } public int scale_factor { get; set; } public Gdk.Paintable? find (Music music, int size = ICON_SIZE) { unowned var cache = size >= GRID_SIZE ? _grid_cache : _icon_cache; return cache.find (music.cover_key); } public void put (Music music, Gdk.Paintable paintable, bool replace = false, int size = ICON_SIZE) { unowned var cache = size >= GRID_SIZE ? _grid_cache : _icon_cache; cache.put (music.cover_key, paintable, replace); } public async Gdk.Paintable? load_async (Music music, int size) { var is_small = size <= GRID_SIZE; if (is_small && !music.replace_qdata (_loading_quark, false, true, null)) { return null; } var pixbuf = yield load_directly_async (music, size); if (is_small) { music.steal_qdata (_loading_quark); } var paintable0 = find (music, size); if (is_small && paintable0 != null) { // Check if already exist with changed cover_key // print ("Already exist: %s\n", music.cover_key); return paintable0; } var paintable = pixbuf != null ? Gdk.Texture.for_pixbuf ((!)pixbuf) : create_music_text_paintable (music); if (is_small) { put (music, paintable, false, size); } else if (pixbuf != null && paintable0 == null) { var minbuf = find_pixbuf_from_cache (music.cover_key); if (minbuf != null) { put (music, Gdk.Texture.for_pixbuf ((!)minbuf), false, ICON_SIZE); } } return paintable; } public async Gdk.Pixbuf? load_directly_async (Music music, int size) { var file = File.new_for_uri (music.uri); var is_native = file.is_native (); if (!_remote_thumbnail && !is_native) { return null; } var album_key_ = @"$(music.album_key)-$(music.artist)-"; var tags = new Gst.TagList?[] { null }; var args = new string[] { music.cover_key, music.cover_uri ?? "", music.get_abbreviation () }; var pixbuf = yield run_async (() => { var tag = tags[0] = parse_gst_tags (file); File? cover_file = null; Gdk.Pixbuf? pixbuf = null; Gst.Sample? sample = null; if (tag != null && (sample = parse_image_from_tag_list ((!)tag)) != null) { // Check if there is an album cover with same artist and image size var image_size = sample?.get_buffer ()?.get_size () ?? 0; var album_key = album_key_ + image_size.to_string ("%x"); check_same_album_cover (album_key, ref args[0]); pixbuf = load_clamp_pixbuf_from_sample ((!)sample, size * _scale_factor); } if (pixbuf == null && (cover_file = _cover_finder?.find (file.get_parent ())) != null) { var album_key = (!) cover_file?.get_path (); args[0] = args[1] = (!) cover_file?.get_uri (); check_same_album_cover (album_key, ref args[0]); pixbuf = load_clamp_pixbuf_from_file ((!)cover_file, size * _scale_factor); } if (pixbuf != null) { var minbuf = find_pixbuf_from_cache (args[0]); if (minbuf == null) { minbuf = create_clamp_pixbuf ((!)pixbuf, ICON_SIZE * _scale_factor); put_pixbuf_to_cache (args[0], (!)minbuf); } return size <= ICON_SIZE ? minbuf : pixbuf; } args[0] = args[2]; return null; // Run in single_thread_pool for samba to save connections }, false, file.has_uri_scheme ("smb")); if (!is_native && tags[0] != null && music.from_gst_tags ((!)tags[0])) { // Update for remote file, since it maybe cached but not parsed from file early tag_updated (music); } if (music.cover_key != args[0]) music.cover_key = args[0]; if (args[1].length > 0) music.cover_uri = args[1]; return pixbuf; } public Gdk.Paintable create_music_text_paintable (Music music) { var text = music.get_abbreviation (); var color_count = BACKGROUND_COLORS.length / 2; var color_index = (text.length == 0 || text == UNKNOWN_ALBUM) ? color_count - 1 : str_hash (text) % (color_count - 1); return create_simple_text_paintable (text, ICON_SIZE, color_index); } public string create_music_text_svg (Music music) { var text = music.get_abbreviation (); var color_count = BACKGROUND_COLORS.length / 2; var color_index = (text.length == 0 || text == UNKNOWN_ALBUM) ? color_count - 1 : str_hash (text) % (color_count - 1); return create_text_svg ((!)_pango_context, text, color_index); } public Gdk.Paintable create_simple_text_paintable (string text, int size, uint color_index = 0x7fffffff) { var paintable = create_text_paintable ((!)_pango_context, text, size * _scale_factor, size * _scale_factor, color_index); return paintable ?? new BasePaintable (); } private void check_same_album_cover (string album_key, ref string cover_key) { unowned string key, uri; lock (_album_covers) { if (_album_covers.lookup_extended (album_key, out key, out uri)) { cover_key = uri; // print ("Same album cover: %s\n", album_key); } else { _album_covers[album_key] = cover_key; } } } private Gdk.Pixbuf? find_pixbuf_from_cache (string cover_key) { lock (_album_pixbufs) { return _album_pixbufs.find (cover_key); } } private void put_pixbuf_to_cache (string cover_key, Gdk.Pixbuf pixbuf) { lock (_album_pixbufs) { _album_pixbufs.put (cover_key, pixbuf); } } } public static Gdk.Pixbuf create_clamp_pixbuf (Gdk.Pixbuf pixbuf, int size) { var width = pixbuf.width; var height = pixbuf.height; if (size > 0 && width > size && height > size) { var scale = width > height ? (size / (double) height) : (size / (double) width); var dx = (int) (width * scale + 0.5); var dy = (int) (height * scale + 0.5); var newbuf = pixbuf.scale_simple (dx, dy, Gdk.InterpType.TILES); if (newbuf != null) return (!)newbuf; } return pixbuf; } public static Gdk.Pixbuf? load_clamp_pixbuf_from_file (File file, int size) { try { var fis = file.read (); var bis = new BufferedInputStream (fis); bis.buffer_size = 16384; return new Gdk.Pixbuf.from_stream_at_scale (bis, size, size, true); } catch (Error e) { } return null; } public static Gdk.Pixbuf? load_clamp_pixbuf_from_sample (Gst.Sample sample, int size) { var buffer = sample.get_buffer (); Gst.MapInfo? info = null; try { if (buffer?.map (out info, Gst.MapFlags.READ) ?? false) { var bytes = new Bytes.static (info?.data); var stream = new MemoryInputStream.from_bytes (bytes); return new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); } } catch (Error e) { } finally { if (info != null) buffer?.unmap ((!)info); } return null; } }