1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-01-16 06:28:02 +00:00

Compare commits

..

296 Commits

Author SHA1 Message Date
Christian Schabesberger
26489b0f00 fix filters 2018-07-22 13:55:17 +02:00
Marc Riera
414abad05f Translated using Weblate (Catalan)
Currently translated at 100,0% (383 of 383 strings)
2018-07-22 12:41:26 +02:00
Heimen Stoffels
735d9a5391 Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 11:40:53 +02:00
Osoitz
50571449cb Translated using Weblate (Basque)
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 11:01:08 +02:00
Weblate
a31aacd115 Merge branch 'origin/dev' into Weblate 2018-07-22 10:57:42 +02:00
Rex_sa
f5b57cc0da Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-22 10:57:42 +02:00
Osoitz
b7006a8f2c Translated using Weblate (Basque)
Currently translated at 100.0% (379 of 379 strings)
2018-07-22 10:57:35 +02:00
Christian Schabesberger
82bb467a2a move on to version v0.13.6 2018-07-21 14:35:03 +02:00
Christian Schabesberger
3a44e92432 Merge branch 'search' into dev 2018-07-19 16:19:33 +02:00
Christian Schabesberger
e60db5f928 make new filtersystem translatable 2018-07-19 16:18:57 +02:00
Ariel Shulman
78485287a4 Translated using Weblate (Hebrew)
Currently translated at 82.3% (312 of 379 strings)
2018-07-18 20:37:59 +02:00
D D
48b6f01b13 Translated using Weblate (Bulgarian)
Currently translated at 97.8% (371 of 379 strings)
2018-07-18 17:35:33 +02:00
Christian Schabesberger
39e04de208 show radio pin for content filter again 2018-07-18 15:05:49 +02:00
Next Hubs
68d5b59693 Translated using Weblate (Urdu)
Currently translated at 5.0% (19 of 379 strings)
2018-07-18 12:43:31 +02:00
Andrea Troiano
4ef01ef745 Translated using Weblate (Italian)
Currently translated at 100,0% (379 of 379 strings)
2018-07-18 10:28:06 +02:00
Rex_sa
573fa8870c Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-17 14:34:21 +02:00
Rex_sa
88d354b08b Translated using Weblate (Arabic)
Currently translated at 100,0% (379 of 379 strings)
2018-07-16 13:17:38 +02:00
Rex_sa
0ff65b5496 Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-15 21:34:22 +02:00
Christian Schabesberger
14e0dcb085 fix names from UIH to LinkHandler 2018-07-15 21:21:09 +02:00
Rex_sa
e008fd21a4 Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-14 21:00:07 +02:00
Nathan Follens
4638149ad0 Translated using Weblate (Flemish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-11 20:52:24 +02:00
Tobias Groza
e386bdd6b3 Translated using Weblate (Greek)
Currently translated at 19.7% (75 of 379 strings)
2018-07-11 18:38:04 +02:00
Christian Schabesberger
decb167ba9 make the new extractor refactorings work with SoundCloud 2018-07-10 16:26:42 +02:00
Tobias Groza
dd9557c13e Translated using Weblate (Greek)
Currently translated at 19.5% (74 of 379 strings)
2018-07-10 02:38:34 +02:00
Lee Hoe Mun
708d7162fb Translated using Weblate (Chinese (Mandarin))
Currently translated at 16.6% (63 of 379 strings)
2018-07-09 20:35:11 +02:00
Michalis Nikolaidis
1ee1b522f1 Translated using Weblate (Greek)
Currently translated at 19,5% (74 of 379 strings)
2018-07-09 02:11:31 +02:00
Christian Schabesberger
d5a500c037 bring everything to compile and run 2018-07-08 17:46:21 +02:00
Eduardo Caron
3e02c65bc0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (379 of 379 strings)
2018-07-08 15:22:38 +02:00
Christian Schabesberger
d10f9a5f25 add getMoreInfo to SearchInfo 2018-07-08 14:45:00 +02:00
AB
17e7214d25 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (379 of 379 strings)
2018-07-08 09:28:02 +02:00
Víctor Manuel Tapia Ramírez
22774592db Translated using Weblate (Spanish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 21:07:00 +02:00
Freddy Morán Jr
7c2aa6e69d Translated using Weblate (Spanish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 21:06:55 +02:00
Ali Demirtas
d1dbcda88e Translated using Weblate (Turkish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 19:03:18 +02:00
Jeff Huang
3e05508cf9 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (379 of 379 strings)
2018-07-07 15:10:47 +02:00
Heimen Stoffels
1acdefd358 Translated using Weblate (Dutch)
Currently translated at 100.0% (379 of 379 strings)
2018-07-07 11:06:57 +02:00
Marc Riera
23143797f9 Translated using Weblate (Catalan)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 10:27:11 +02:00
ssantos
3364bf268b Translated using Weblate (German)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 07:51:52 +02:00
Weblate
2025d6305e Merge branch 'origin/dev' into Weblate 2018-07-07 07:48:38 +02:00
cxj
639ad7698d Translated using Weblate (Chinese (Mandarin))
Currently translated at 15,6% (59 of 378 strings)
2018-07-07 07:48:37 +02:00
D D
4eaff51ba2 Translated using Weblate (Bulgarian)
Currently translated at 61,9% (234 of 378 strings)
2018-07-07 07:48:35 +02:00
ssantos
1fb30bc3d9 Translated using Weblate (German)
Currently translated at 100,0% (378 of 378 strings)
2018-07-07 07:48:33 +02:00
Christian Schabesberger
6b66f40bcb Merge pull request #1392 from karyogamy/exoplayer-2.8.0-update
ExoPlayer 2.8.2 Update
2018-07-05 13:02:21 +02:00
Allan Nordhøy
a3cd531cc8 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.0% (363 of 378 strings)
2018-07-04 00:40:31 +02:00
Nathan Follens
55c84192be Translated using Weblate (Flemish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-03 09:36:55 +02:00
Víctor Manuel Tapia Ramírez
aa9018b97b Translated using Weblate (Spanish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-03 00:43:30 +02:00
monolifed
1e79c146a7 Translated using Weblate (Turkish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-02 15:43:34 +02:00
Marian Hanzel
cb8545e33d Translated using Weblate (Slovak)
Currently translated at 96.0% (363 of 378 strings)
2018-07-02 09:44:01 +02:00
Nathan Follens
4a484c535b Translated using Weblate (Flemish)
Currently translated at 100,0% (378 of 378 strings)
2018-07-02 09:27:09 +02:00
Víctor Manuel Tapia Ramírez
ee417e41ea Translated using Weblate (Spanish)
Currently translated at 100,0% (378 of 378 strings)
2018-07-02 00:16:04 +02:00
Eduardo Caron
3cdc1fcaee Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 22:45:25 +02:00
Jeff Huang
0a023a61f9 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 15:21:02 +02:00
monolifed
73f81c5b52 Translated using Weblate (Turkish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 14:48:03 +02:00
Marc Riera
bc4acbb7e1 Translated using Weblate (Catalan)
Currently translated at 100,0% (378 of 378 strings)
2018-07-01 12:39:58 +02:00
Heimen Stoffels
26d07ea2c6 Translated using Weblate (Dutch)
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 11:40:26 +02:00
Weblate
5380c8352d Merge branch 'origin/dev' into Weblate 2018-07-01 08:42:10 +02:00
Anton Shestakov
e85e91183e Translated using Weblate (Russian)
Currently translated at 99.4% (371 of 373 strings)
2018-07-01 08:42:07 +02:00
Christian Schabesberger
a208a22bc2 add link to the incredible bugreport converter 2018-06-29 14:03:15 +02:00
John Zhen Mo
644766b5a6 -Updated exoplayer lib to 2.8.2. 2018-06-28 12:21:03 -07:00
John Zhen Mo
ca679f5932 -Fixed potential NPE when updating thumbnail in background player. 2018-06-28 12:18:02 -07:00
John Zhen Mo
7f7145e8de -Fixed playback parameter dialog settings not persisting through rotation.
-Moved playback parameter dialog step size selector to below pitch slider.
2018-06-28 12:04:30 -07:00
John Zhen Mo
aa1878c15a -Changed baseplayer metadata getters to use media tag as source.
-Changed background player notification to no longer update bitmap on progress time change.
-Changed popup player to move above soft keyboard when it is opened.
2018-06-28 12:04:30 -07:00
John Zhen Mo
e7d23176b7 -Fixed database backup failing due to journal file name change after Room DB version update. 2018-06-28 12:04:30 -07:00
John Zhen Mo
31218c2a8c -Fixed popup player notification metadata not updated on stream change.
-Fixed popup player window not clipped to above soft input keyboard upon expansion.
2018-06-28 12:03:20 -07:00
John Zhen Mo
06374c82fd -Fixed video players end screen not cleared on restarting playback after single stream play queue is completed. 2018-06-28 12:03:20 -07:00
John Zhen Mo
8efcc8f80f -Fixed main video player end screen thumbnail not fitting screen aspect ratio. 2018-06-28 12:03:20 -07:00
John Zhen Mo
2d6317bd24 -Fixed audio-only streams thumbnail not displaying on video players.
-Fixed potential play queue desynchronization due to fast forwarding on silence.
-Added current thumbnail storing in base player to allow immediate retrieval for notification building.
-Removed video player buffer spinner during interim buffering but not initial buffering.
-Reverted foreground notification stopping on pause and on complete.
2018-06-28 12:03:20 -07:00
John Zhen Mo
157b064214 -Fixed player database and progress disposable disposed when destroying exoplayer.
-Fixed livestream not reloading on behind live window exception.
-Added nonnull annotation to player intent strings.
2018-06-28 12:01:34 -07:00
John Zhen Mo
0ece4851d2 -Updated ExoPlayer to 2.8.1, fixing livestream with long duration not loading.
-Updated OkHttp to 3.10.0 and RxJava to 2.1.14.
-Changed player recovery seek to use ExoPlayer built-in window seeking instead of seeking after stream window starts playing.
-Changed playback speed changer default step size to 25%.
-Changed player notification to reset on all state changes.
-Fixed gradle dependency version incorrect variable names.
-Fixed video player double tap not working during pause.
-[#1412] Fixed NPE when sharing video to main video activity when it was playing but is out of focus: Reset main player state when new intent is received.
-[#1410] Fixed fast forwarding and rewinding not working within 10 seconds from beginning or end of a stream window.
2018-06-28 12:01:34 -07:00
John Zhen Mo
f1f5996975 -Refactored playback resolvers and other persistent player objects to instantiate once only during player creation to enforce non-nullity.
-Fixed background and popup player service staying in foreground when playback is paused or completed.
-Fixed player metadata not updating on new stream.
-Fixed player intent playback quality not applied.
-Fixed player auto-queue not applied after stream transition or swapping.
2018-06-28 12:00:00 -07:00
John Zhen Mo
0a2dbc4688 -Fixed playlist fragment infinite update cycle.
-Updated Room DB version to 1.1.0.
2018-06-28 11:59:59 -07:00
John Zhen Mo
13587d7ab3 -Fixed some typos. 2018-06-28 11:58:33 -07:00
John Zhen Mo
0fcef064fb -Reduced fling speed required to close popup by 40%. 2018-06-28 11:58:33 -07:00
John Zhen Mo
19b8796cbc -Fixed statistics fragment button not animating when pressed.
-Removed background player notification button opacity change.
2018-06-28 11:58:33 -07:00
John Zhen Mo
15fb60a845 -Fixed bookmarked playlist not updating metadata when changed. 2018-06-28 11:58:32 -07:00
John Zhen Mo
5c202f04e7 -[#1383]Fixed popup player caption selector not populating due to full width aspect ratio selector.
-Fixed potential memory leak in media session connector containing player instance.
2018-06-28 11:58:32 -07:00
John Zhen Mo
bc6fdf81d2 -Refactored player media source resolution into external helpers.
-Baked resolved media metadata into media source for one-way data passing.
2018-06-28 11:58:32 -07:00
John Zhen Mo
3194a2bf2c -Fixed skip silence state not maintained by player on new queue.
-Fixed TrackSelector deprecations.
2018-06-28 11:58:32 -07:00
John Zhen Mo
72d1e5131f -Added skip silence toggle to playback speed control.
-Added step size selector to playback speed control.
-Added skip silence flag to player intents.
-Moved default preset in playback speed control to neutral dialog button, renamed as reset.
-Removed nightcore preset from playback speed control.
2018-06-28 11:58:32 -07:00
John Zhen Mo
7721098551 -Updated ExoPlayer to 2.8.0
-Updated MediaSource contracts in ManagedMediaSource.
-Changed PlaceholderMediaSource and FailedMediaSource to use built-in BaseMediaSource implementation.
-Changed deprecated DynamicConcatenatingMediaSource to ConcatenatingMediaSource.
-Removed manual playlist media source disposal in favor of player built-in disposal.
2018-06-28 11:58:32 -07:00
Christian Schabesberger
f563bc4210 Merge pull request #1510 from karyogamy/lib-update
Library version update and database backup fix
2018-06-28 12:16:04 +02:00
Christian Schabesberger
43e7be9b86 Merge pull request #1509 from karyogamy/main-video-player-fix
Main video player fix
2018-06-28 12:00:54 +02:00
John Zhen Mo
27131d15dd -Updated room db to 1.1.1.
-Fixed database import/export to no longer include accessory db files to ensure backward compatibility.
2018-06-26 12:26:01 -07:00
John Zhen Mo
fb1a290bd9 -Updated okHttp to 3.10.0.
-Updated mockito to 2.8.9.
-Updated rxJava to 2.1.14.
-Fixed stetho to use correct lib version.
2018-06-26 12:02:26 -07:00
John Zhen Mo
ef16145695 -Fixed player new share intent causing main player crash due to player activity in background. 2018-06-26 10:21:43 -07:00
John Zhen Mo
4fbd1182c2 -Fixed minimizing to popup player does not destroying existing player when drawing over app permission is not granted. 2018-06-26 10:19:16 -07:00
Christian Schabesberger
2d39e65b5c Merge branch '640-screen-off' of https://github.com/krtkush/NewPipe into test 2018-06-26 11:23:45 +02:00
Kartikey Kushwaha
8e96b675fa Removed unwanted files. 2018-06-26 01:13:21 +05:30
Christian Schabesberger
adb6943420 Merge pull request #1354 from karyogamy/minimize-on-exit
Minimize main player on exit
2018-06-25 19:46:13 +02:00
Christian Schabesberger
eae7babf93 Merge pull request #1454 from Somethingweirdhere/dev
Added share option to long tap menu
2018-06-25 15:28:37 +02:00
Christian Schabesberger
7d5e18c05b Merge pull request #1498 from Somethingweirdhere/download
Download option in share menu
2018-06-25 12:35:34 +02:00
Somethingweirdhere
cbe001efd6 Added option to menu 2018-06-25 12:04:11 +02:00
Christian Schabesberger
86b783fb0f Merge pull request #1472 from acrosca/undo_delete
undo delete
2018-06-25 10:04:17 +02:00
Christian Schabesberger
ccc27b48df Merge pull request #1497 from karyogamy/view-history-fix
View history fix
2018-06-25 09:26:08 +02:00
Tobias Groza
a32391f560 Translated using Weblate (German)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 17:38:37 +02:00
nautilusx
aed0348802 Translated using Weblate (German)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 17:38:32 +02:00
Rossinière Vaud
1b66446c0d Translated using Weblate (Polish)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 12:42:40 +02:00
Tobias Groza
2bc0c8a483 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:58 +02:00
DafabHoid
3f7e02e305 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:53 +02:00
nautilusx
dd0d666003 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:47 +02:00
Tobias Groza
81859a37de Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:05:55 +02:00
rimasx
180bb581a3 Translated using Weblate (Estonian)
Currently translated at 94.6% (353 of 373 strings)
2018-06-23 16:37:11 +02:00
yuriqdev
2f31779af4 Translated using Weblate (Russian)
Currently translated at 99.7% (372 of 373 strings)
2018-06-23 13:42:42 +02:00
Rossinière Vaud
ee6d512165 Translated using Weblate (Polish)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 12:08:23 +02:00
Weblate
1470fdc057 Merge branch 'origin/dev' into Weblate 2018-06-22 09:35:50 +02:00
rimasx
93bbaf187e Translated using Weblate (Estonian)
Currently translated at 94.6% (353 of 373 strings)
2018-06-22 09:35:46 +02:00
Víctor Manuel Tapia Ramírez
f1e43007f1 Translated using Weblate (Chinese (Mandarin))
Currently translated at 6.4% (24 of 373 strings)
2018-06-22 09:35:21 +02:00
D D
155436b85d Translated using Weblate (Bulgarian)
Currently translated at 62.7% (234 of 373 strings)
2018-06-22 09:35:18 +02:00
Somethingweirdhere
f3e029c3f6 Cleaned code, downloaddialog now also appears after giving storage permission. 2018-06-20 14:46:57 +02:00
Christian Schabesberger
90d6416f55 Merge pull request #1491 from annoyatron255/video-info-fix
Fix #1440 Broken Video Info Layout
2018-06-19 09:50:02 +02:00
Ale-Ma
af2a2e45af Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-19 09:40:03 +02:00
Andrea Troiano
e79eda9c5c Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-19 09:39:58 +02:00
John Zhen Mo
b338d9dbcf -Fixed view not registered when playback is started on external players. 2018-06-18 18:27:37 -07:00
John Zhen Mo
7fb9345344 -Fixed remote playlist metadata not updated when remote source data has changed. 2018-06-18 18:22:52 -07:00
Weblate
77b488568b Merge branch 'origin/dev' into Weblate 2018-06-17 15:56:31 +02:00
Edwar Tikhonov
fcf650c6eb Translated using Weblate (Russian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-17 15:56:30 +02:00
rimasx
0c21023ad8 Added translation using Weblate (Estonian) 2018-06-17 15:56:28 +02:00
Somethingweirdhere
8f35a56ec8 Added download to share menu 2018-06-17 13:55:43 +02:00
annoyatron255
95ba1873e4 Fix #1440 Broken Video Info Layout 2018-06-16 18:12:56 -05:00
Andrei.Rosca
8b8652d44c undo delete - code format 2018-06-15 16:15:55 +02:00
Issam Maghni
2515b8167f Disable animation (hidden anyway by navigation) 2018-06-15 01:21:30 -04:00
Andrei.Rosca
09dd044f3d undo delete 2018-06-13 09:07:57 +02:00
Dual Natan
ace0ed9667 Translated using Weblate (Swedish)
Currently translated at 68.9% (257 of 373 strings)
2018-06-12 22:43:59 +02:00
Dual Natan
cf03708da2 Translated using Weblate (Macedonian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-11 21:21:57 +02:00
Edwar Tikhonov
d4670bf6fa Translated using Weblate (Russe)
Currently translated at 100,0% (373 of 373 strings)
2018-06-11 16:04:29 +02:00
Edwar Tikhonov
feea448a24 Translated using Weblate (Русский)
Currently translated at 100,0% (373 of 373 strings)
2018-06-10 15:17:20 +02:00
aiddroid
71ad54652b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (373 of 373 strings)
2018-06-10 10:35:41 +02:00
aiddroid
8e1deda7b0 Translated using Weblate (Chinese (Mandarin))
Currently translated at 0.0% (0 of 373 strings)
2018-06-10 09:35:42 +02:00
aiddroid
752f985e13 Translated using Weblate (简体中文(Chinese Simplified))
Currently translated at 100.0% (373 of 373 strings)
2018-06-09 10:09:35 +02:00
Kartikey Kushwaha
89e3219e06 Further fixes wrt FLAG_NOT_FOCUSABLE. 2018-06-09 01:38:57 +05:30
Kartikey Kushwaha
429dddc6c9 reintroduced hideControls method. 2018-06-09 01:13:37 +05:30
Kartikey Kushwaha
981174a490 Fixed bug #640. 2018-06-09 01:01:13 +05:30
Somethingweirdhere
201f7e9848 Added share option to Popup and Background queues 2018-06-08 15:59:05 +02:00
kapodamy
bb0d8ad58a Translated using Weblate (Spanish)
Currently translated at 99.7% (372 of 373 strings)
2018-06-08 01:44:11 +02:00
Weblate
29e64f7c1a Merge branch 'origin/dev' into Weblate 2018-06-06 20:37:29 +02:00
JAPP
4819ebd56e Translated using Weblate (French)
Currently translated at 99.1% (370 of 373 strings)
2018-06-06 20:37:23 +02:00
John Zhen Mo
3b603b0637 -Added back button press check to destroy rather than minimize main video player. 2018-06-05 23:37:20 -07:00
Somethingweirdhere
baa63249d1 Added share option to long tap menu 2018-06-05 19:48:31 +02:00
Somethingweirdhere
ee347e3081 Merge pull request #1 from TeamNewPipe/dev
Up to date
2018-06-05 19:35:47 +02:00
Christian Schabesberger
f96bc95053 Update CONTRIBUTING.md 2018-06-05 11:03:48 +02:00
John Zhen Mo
e1df4757e4 -Expanded minimize to exit to allow resuming on background player.
-Modified minimize to exit toggle to selection dialog.
2018-06-03 14:09:16 -07:00
John Zhen Mo
4fc37a7321 -Added toggle to allow main video player to switch to popup player when onstop is called.
-Fixed player state not recovering when player is stopped during multiwindow mode.
-Updated gradle to 3.1.2.
2018-06-03 13:20:39 -07:00
Christian Schabesberger
2a45a13f73 move on to version v0.13.5 2018-06-03 12:36:35 +02:00
Christian Schabesberger
067b15c300 Merge branch 'dev' of https://github.com/DafabHoid/NewPipe into test 2018-06-03 12:20:43 +02:00
Christian Schabesberger
8a1c283542 Merge branch 'media-session-fix' of https://github.com/karyogamy/NewPipe into test 2018-06-03 12:12:54 +02:00
Christian Schabesberger
93d1e8b2ff Merge pull request #1441 from DafabHoid/fixdownloadercrashrelease
Downloader: Fix crash on loading unfinished downloads from .giga files (Fixup for release builds)
2018-06-03 12:08:20 +02:00
Christian Schabesberger
c60d5b54fa Merge branch 'remeber_brightness' of https://github.com/acrosca/NewPipe into test 2018-06-03 11:02:49 +02:00
Andrei.Rosca
ef180f082e Remember brightness for the session 2018-06-02 09:06:40 +02:00
Rex_sa
f52741cc37 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-06-02 07:34:24 +02:00
DafabHoid
2a2661f066 Downloader: Fix crash on loading unfinished downloads from .giga files
This is a fixup, which fixes the crash in release builds, too. It keeps proguard from removing the new method "private void readObject(ObjectInputStream)", which is only called by the VM, but not from the code.
2018-06-01 14:35:03 +02:00
Rex_sa
b521903138 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-06-01 07:02:51 +02:00
Ali Toor
acbd699d95 Translated using Weblate (Urdu)
Currently translated at 2.9% (11 of 373 strings)
2018-05-31 06:43:02 +02:00
Andrea Troiano
6b8928becb Translated using Weblate (Italiano)
Currently translated at 100,0% (373 of 373 strings)
2018-05-30 11:14:38 +02:00
Víctor Manuel Tapia Ramírez
e393bdb1e5 Translated using Weblate (Spanish)
Currently translated at 99.7% (372 of 373 strings)
2018-05-30 10:43:28 +02:00
Osoitz
bba6b96765 Translated using Weblate (Basque)
Currently translated at 100.0% (373 of 373 strings)
2018-05-29 16:58:04 +02:00
John Zhen Mo
740116356c -Fixed media session activation.
-Removed redundant setShuffle call in media session callback and its user.
-Removed unused dummy playback preparer.
2018-05-28 20:02:02 -07:00
monolifed
2f6e4fa4a3 Translated using Weblate (Turkish)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 20:44:52 +02:00
Allan Nordhøy
fb3f6721b2 Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.9% (358 of 373 strings)
2018-05-28 17:41:18 +02:00
AB
4e7bd21e5c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 17:07:13 +02:00
Jeff Huang
219c2030b9 Translated using Weblate (漢語(正體字))
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 08:56:18 +02:00
Marc Riera
b75fdb4566 Translated using Weblate (català)
Currently translated at 100,0% (373 of 373 strings)
2018-05-28 08:41:52 +02:00
Nathan Follens
4584b14a31 Translated using Weblate (Vlaams)
Currently translated at 100,0% (373 of 373 strings)
2018-05-28 07:53:32 +02:00
thami simo
814ddb5932 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 07:45:58 +02:00
DafabHoid
6ea0f6290a Downloader: Notify the progress every 64K instead of every 512 Bytes
This improves downloading performance dramatically when cpu bound:
Before, even a high-end cpu from 2013 can't download faster than around 1MB/s.
The bigger read buffer size removes the need for a dedicated BufferedInputStream.
2018-05-28 01:07:30 +02:00
monolifed
c796fe1fe6 Translated using Weblate (Turkish)
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 19:49:08 +02:00
Heimen Stoffels
a452a164e6 Translated using Weblate (Dutch)
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 18:00:16 +02:00
ssantos
0d9dd69b19 Translated using Weblate (Deutsch)
Currently translated at 100,0% (373 of 373 strings)
2018-05-27 17:51:41 +02:00
Eduardo Caron
d7b73c18f1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 15:14:19 +02:00
Weblate
07f66c0e45 Merge branch 'dev' into weblate-merge-tmp 2018-05-27 15:11:40 +02:00
monolifed
d449acbf86 Translated using Weblate (Turkish)
Currently translated at 99.4% (369 of 371 strings)
2018-05-27 15:11:40 +02:00
Allan Nordhøy
fce416ba76 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.2% (357 of 371 strings)
2018-05-27 15:11:35 +02:00
dadosch
cb6bfe8556 Translated using Weblate (German)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 15:11:35 +02:00
Florian
d7b31e1d25 Translated using Weblate (French)
Currently translated at 98.6% (366 of 371 strings)
2018-05-27 15:11:34 +02:00
Eduardo Caron
85057376d6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 15:11:31 +02:00
Christian Schabesberger
c43ac7c869 fix conflict 2018-05-27 13:30:23 +02:00
Heimen Stoffels
b2657315f1 Translated using Weblate (Dutch)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 12:36:08 +02:00
ezjerry liao
de5ed9717c Translated using Weblate (漢語(正體字))
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 01:18:13 +02:00
Ali Demirtas
e17a6cbb9f Translated using Weblate (Turkish)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 00:07:08 +02:00
Marc Riera
8e783b774b Translated using Weblate (català)
Currently translated at 100,0% (371 of 371 strings)
2018-05-27 00:03:34 +02:00
AB
733663f40d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (371 of 371 strings)
2018-05-26 22:34:26 +02:00
Ali Demirtas
4b2a792a62 Translated using Weblate (Türkçe)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 21:01:30 +02:00
dadosch
f7aa171d01 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:14:27 +02:00
dadosch
5eafefb683 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:12:02 +02:00
dadosch
5d1b02a856 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:08:29 +02:00
dadosch
4acda3d9ae Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:07:50 +02:00
dadosch
643e10ace2 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:04:45 +02:00
dadosch
7b64a232de Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:00:23 +02:00
dadosch
d7472d837d Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 14:57:32 +02:00
dadosch
94b473ab4b Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:56:36 +02:00
dadosch
4a05bbb6c8 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:45:26 +02:00
Linux User
0343659b35 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:45:21 +02:00
Nathan Follens
f38eadbe30 Translated using Weblate (Vlaams)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 09:32:42 +02:00
Nathan Follens
eb29a53ac5 Translated using Weblate (Nederlands)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 09:29:31 +02:00
Weblate
bc71e260e2 Merge branch 'dev' into weblate-merge-tmp 2018-05-26 08:38:30 +02:00
Bogdan Khomutsky
ddf23a3443 Translated using Weblate (Russian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-26 08:38:30 +02:00
Dual Natan
d41b248d1c Translated using Weblate (Macedonian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-26 08:38:29 +02:00
SN
a025b25933 Translated using Weblate (Hindi)
Currently translated at 83.0% (303 of 365 strings)
2018-05-26 08:38:28 +02:00
Charlotte Lewer
c9a52a6088 Translated using Weblate (Esperanto)
Currently translated at 25.2% (92 of 365 strings)
2018-05-26 08:38:28 +02:00
My Account
0276dca406 Translated using Weblate (Hindi)
Currently translated at 83.0% (303 of 365 strings)
2018-05-26 08:38:22 +02:00
Christian Schabesberger
3bb95ad44c add changelog for version v0.13.4 2018-05-25 18:38:07 +02:00
Christian Schabesberger
0a6572c282 roll back to more stable version of newpipe extractor 2018-05-25 18:29:30 +02:00
Christian Schabesberger
3937067be1 move on to version 0.13.4 2018-05-25 09:45:22 +02:00
Christian Schabesberger
48e4eb44f2 remove unused imports 2018-05-25 09:43:28 +02:00
TobiGr
c78cc6f2fd Add dialog to accept privacy policy before sending crash report
Add link to privacy policy in about fragment
Replace some onClickListeners with Lamdas
2018-05-25 09:29:14 +02:00
Bogdan Khomutsky
73a71e0f5c Translated using Weblate (Russian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-25 08:22:43 +02:00
SINUS (সাইনাস)
93605774f0 Translated using Weblate (Bengali (Bangladesh))
Currently translated at 40.0% (146 of 365 strings)
2018-05-24 18:34:46 +02:00
Daria Szatan
2834e5d78f Translated using Weblate (Polish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-23 19:53:07 +02:00
Allan Nordhøy
dd467b4d63 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.1% (351 of 365 strings)
2018-05-22 11:40:49 +02:00
SN
1fa541776b Translated using Weblate (Hindi)
Currently translated at 80.0% (292 of 365 strings)
2018-05-22 08:37:59 +02:00
Weblate
f6f67c7b0a Merge branch 'dev' into weblate-merge-tmp 2018-05-21 06:37:51 +02:00
lartial
9b3f19c19b Translated using Weblate (Indonesian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-21 06:37:49 +02:00
Christian Schabesberger
f4a9ec15e8 Merge pull request #1407 from DafabHoid/dev
Downloader: Fix crash on loading unfinished downloads from .giga files
2018-05-19 16:19:33 +02:00
Osoitz
006c4ecb02 Translated using Weblate (Basque)
Currently translated at 100.0% (365 of 365 strings)
2018-05-19 10:34:39 +02:00
Nishargo Nigar
1c752b0e18 Translated using Weblate (Bengali (Bangladesh))
Currently translated at 34.2% (125 of 365 strings)
2018-05-19 08:34:45 +02:00
AB
f84aff63e3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 19:06:33 +02:00
DafabHoid
ae8121b680 Utility: Buffer the output to files when serializing 2018-05-18 18:23:32 +02:00
DafabHoid
882fbf9275 Fix crash on loading not yet finished downloads from .giga files 2018-05-18 18:18:37 +02:00
Dual Natan
0a1743251e Translated using Weblate (Macedonian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 11:49:10 +02:00
Osoitz
3403a127c1 Translated using Weblate (Basque)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 10:24:13 +02:00
Robson Cassiano
9c5ca9f09d Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (365 of 365 strings)
2018-05-17 16:40:02 +02:00
David Adrião
73eea5608a Translated using Weblate (Portuguese)
Currently translated at 86.3% (315 of 365 strings)
2018-05-17 13:40:38 +02:00
Osoitz
f48aeb91f4 Translated using Weblate (Basque)
Currently translated at 98.9% (361 of 365 strings)
2018-05-17 11:34:34 +02:00
Robson Cassiano
0bda964ece Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (365 of 365 strings)
2018-05-16 16:21:22 +02:00
Ale-Ma
14f5d54b50 Translated using Weblate (Italian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-16 00:13:39 +02:00
Freddy Morán Jr
105ac2f6ff Translated using Weblate (Spanish)
Currently translated at 99.4% (363 of 365 strings)
2018-05-15 20:43:45 +02:00
HashikDonthineni
160560f1fd Translated using Weblate (Telugu)
Currently translated at 35.3% (129 of 365 strings)
2018-05-15 17:42:23 +02:00
ditokp
deeb667d6f Translated using Weblate (Indonesian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-15 13:48:13 +02:00
Ali Demirtas
78123ff6f5 Translated using Weblate (Turkish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 21:09:52 +02:00
9b1fdff22f Translated using Weblate (Romanian)
Currently translated at 82.7% (302 of 365 strings)
2018-05-14 19:46:37 +02:00
Ciprian
0275502796 Translated using Weblate (Romanian)
Currently translated at 82.7% (302 of 365 strings)
2018-05-14 19:46:32 +02:00
Weblate
8af475e319 Merge branch 'dev' into weblate-merge-tmp 2018-05-14 13:34:29 +02:00
Allan Nordhøy
2202c8f09e Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.8% (350 of 365 strings)
2018-05-14 13:34:28 +02:00
ScratchBuild
adf309d3a8 Translated using Weblate (Japanese)
Currently translated at 77.5% (283 of 365 strings)
2018-05-14 13:34:26 +02:00
Marc Riera
3071314586 Translated using Weblate (Catalan)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 13:34:24 +02:00
thami simo
88e1df840d Translated using Weblate (Arabic)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 13:34:21 +02:00
Christian Schabesberger
23c1b66f6c add note to contribution description 2018-05-14 13:24:35 +02:00
ezjerry liao
b0318a1cce Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 11:52:01 +02:00
Nathan Follens
c130a66e4d Translated using Weblate (Flemish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 18:44:26 +02:00
ssantos
3386ba6d1b Translated using Weblate (German)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 16:41:08 +02:00
Marc Riera
9275569fa6 Translated using Weblate (Catalan)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 13:50:07 +02:00
Heimen Stoffels
2b23dfd0a6 Translated using Weblate (Dutch)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 13:46:49 +02:00
thami simo
2a13d9990e Translated using Weblate (Arabic)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 12:37:46 +02:00
Weblate
c9669b51c6 Merge branch 'dev' into weblate-merge-tmp 2018-05-13 11:34:46 +02:00
Osoitz
486c180b3c Translated using Weblate (Basque)
Currently translated at 98.9% (360 of 364 strings)
2018-05-13 11:34:43 +02:00
Christian Schabesberger
9eb5bf9b87 Merge pull request #1375 from acrosca/code_inspection
Code inspection
2018-05-12 14:21:37 +02:00
Christian Schabesberger
953a89f3a1 Merge branch 'settingsExport' of https://github.com/Somethingweirdhere/NewPipe into test 2018-05-12 13:34:05 +02:00
Christian Schabesberger
d638fa1434 use commit from newpipeextractor master 2018-05-11 18:00:28 +02:00
Christian Schabesberger
e6d700288c fix afiliate parse link failure 2018-05-11 18:00:28 +02:00
Christian Schabesberger
371f14cdc9 make compartible to yoututbe service restructure 2018-05-11 18:00:28 +02:00
Christian Schabesberger
0733ae2404 make compatible with encosing urlidhandler commit 2018-05-11 18:00:28 +02:00
ButterflyOfFire
b1731ebd49 Translated using Weblate (French)
Currently translated at 99.1% (361 of 364 strings)
2018-05-11 17:37:14 +02:00
Somethingweirdhere
342b3191ac Changed to lambda convention 2018-05-11 17:17:07 +02:00
zmni
cd39445245 Translated using Weblate (Indonesian)
Currently translated at 91.2% (332 of 364 strings)
2018-05-11 16:39:32 +02:00
Freddy Morán Jr
92eac67367 Translated using Weblate (Spanish)
Currently translated at 97.2% (354 of 364 strings)
2018-05-10 19:41:12 +02:00
Edwar Tikhonov
0e31a0c704 Translated using Weblate (Russian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:42:55 +02:00
gensitu
d60c117a70 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:36:24 +02:00
thami simo
69ccad5998 Translated using Weblate (Arabic)
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:34:28 +02:00
Edwar Tikhonov
b6d22320e6 Translated using Weblate (Russian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-09 11:12:45 +02:00
Weblate
d3bf948dba Merge remote-tracking branch 'origin/dev' into dev 2018-05-09 09:50:28 +02:00
ditokp
5de3d96b31 Translated using Weblate (Indonesian)
Currently translated at 85.7% (312 of 364 strings)
2018-05-09 09:50:28 +02:00
Florian
d30dd64322 Translated using Weblate (French)
Currently translated at 99.1% (361 of 364 strings)
2018-05-09 09:50:27 +02:00
thami simo
386df10a5a Translated using Weblate (Arabic)
Currently translated at 100.0% (364 of 364 strings)
2018-05-09 09:50:25 +02:00
Christian Schabesberger
af147de547 upgrade gradle 2018-05-08 20:56:11 +02:00
23cd0e5a5e Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 17:30:05 +02:00
c5a566657c Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:21:31 +02:00
Alexander Sparzt
ce2018c864 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:21:26 +02:00
472ab46af2 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:18:30 +02:00
Alexander Sparzt
7f87d45bb5 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:18:25 +02:00
e9f7ab18bb Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:16:29 +02:00
Alexander Sparzt
fba83a8afe Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:16:24 +02:00
nailyk
05089abddc Translated using Weblate (French)
Currently translated at 96.4% (351 of 364 strings)
2018-05-08 14:43:20 +02:00
Prabjot Singh
ccd70aac51 Translated using Weblate (Punjabi)
Currently translated at 5.2% (19 of 364 strings)
2018-05-08 07:41:59 +02:00
nailyk
5df8445d04 Translated using Weblate (French)
Currently translated at 96.1% (350 of 364 strings)

Peut-être existe des traductions existantes mais je ne les aies pas trouvées.
2018-05-07 18:17:34 +02:00
nailyk
8c43674fa4 Translated using Weblate (French)
Currently translated at 95.8% (349 of 364 strings)
2018-05-07 18:14:23 +02:00
Florent Peterschmitt
f162316a6b Translated using Weblate (French)
Currently translated at 94.5% (344 of 364 strings)
2018-05-07 14:37:09 +02:00
Andrea Troiano
670596ed88 Translated using Weblate (Italian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-07 10:08:51 +02:00
Prabjot Singh
6b3eb716c4 Added translation using Weblate (Punjabi) 2018-05-07 06:39:45 +02:00
Emin Tufan Çetin
6a5180d94c Translated using Weblate (Turkish)
Currently translated at 100.0% (364 of 364 strings)
2018-05-06 17:16:30 +02:00
Florent Peterschmitt
83faaedfcc Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:54:09 +02:00
Florian
d98d790a7a Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:54:02 +02:00
Florian
36b5833a3a Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:51:12 +02:00
5e86781a79 Translated using Weblate (French)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:27:25 +02:00
Florian
6a780504b4 Translated using Weblate (French)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:27:20 +02:00
Weblate
a0d8212136 Merge remote-tracking branch 'origin/dev' into dev 2018-05-06 13:01:58 +02:00
Marian Hanzel
0e1e6a9d62 Translated using Weblate (Slovak)
Currently translated at 95.8% (349 of 364 strings)
2018-05-06 13:01:58 +02:00
thami simo
812282a332 Translated using Weblate (Arabic)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:01:56 +02:00
r2308145
a8a4c9e97f Translated using Weblate (Czech)
Currently translated at 100.0% (364 of 364 strings)
2018-05-06 13:01:53 +02:00
Andrei.Rosca
24c293e335 fix context leaks 2018-05-06 10:50:02 +02:00
Andrei.Rosca
0a596df497 default ViewHolder 2018-05-06 10:14:24 +02:00
Andrei.Rosca
3d66c6572b prevent infinite loop 2018-05-06 10:08:56 +02:00
Andrei.Rosca
f45769cbb2 Reduce overdraw 2018-05-05 10:26:35 +02:00
Weblate
ef51f93c6f Merge remote-tracking branch 'origin/dev' into dev 2018-05-05 09:02:21 +02:00
ssantos
35af68f148 Translated using Weblate (German)
Currently translated at 100.0% (364 of 364 strings)
2018-05-05 09:02:19 +02:00
James Straub
646fa877ba Update to mobile data limiting
- Moved non-key strings from string_keys.xml to strings.xml
- Code style changes
- Replaced a hard coded key string with resource constant
2018-04-22 10:20:19 -04:00
James Straub
d1b0cd74be Added the ability to limit video quality if using mobile data.
* Added a dropdown to video & audio settings
* Changes to ListHelper:
** Limits resolution when code requests the default video resolution
** Limits audio bitrate when code requests the default audio bitrate
** Removed some dead code and did some cleanup
** Make methods private/protected to help understand what was in use
** The code now chooses one format over an other using a simple raking system defined in array constants. I realized I needed to do this in order to choose the most efficient video stream. I did my best to evaluate the video and audio formats based on quality and efficiency. It's not an exact science.
** Made changes to the tests to support my changes
2018-04-21 12:35:04 -04:00
Somethingweirdhere
dcdb2c323e Added settings export 2018-04-19 01:31:25 +02:00
Somethingweirdhere
d9e616beee Fixed crash when trying to open a downloaded file without a player 2018-04-17 22:26:24 +02:00
121 changed files with 5348 additions and 2050 deletions

View File

@@ -15,6 +15,8 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome!
* We use English for development. Issues in other languages will be closed and ignored.
* Please only add *one* issue at a time. Do not put multiple issues into one thread.
* When reporting a bug please give us a context, and a description how to reproduce it.
* Issues that only contain a generated bug report, but no describtion might be closed.
## Bug Fixing
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information.
@@ -28,7 +30,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* Stick to NewPipe's style conventions (well, just look the other code and then do it the same way :))
* Do not bring non-free software (e.g., binary blobs) into the project. Also, make sure you do not introduce Google libraries.
* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy)
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might not be considered, GitHub is the primary platform.
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might not be considered, GitHub is the primary platform. (This only affects you if you are a member of TeamNewPipe)
* When submitting changes, you confirm that your code is licensed under the terms of the [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
* Please test (compile and run) your code before you submit changes! Ideally, provide test feedback in the PR description. Untested code will **not** be merged!
* Try to figure out yourself why builds on our CI fail.

View File

@@ -1,2 +1,3 @@
- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
- [ ] I checked if the issue/feature exists in the latest version.
- [ ] I did use the [incredible bugreport to markdown converter](https://teamnewpipe.github.io/CrashReportToMarkdown/) to paste bug reports.

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 27
versionCode 62
versionName "0.13.3"
versionCode 65
versionName "0.13.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -42,10 +42,10 @@ android {
ext {
supportLibVersion = '27.1.1'
exoPlayerLibVersion = '2.7.3'
roomDbLibVersion = '1.0.0'
exoPlayerLibVersion = '2.8.2'
roomDbLibVersion = '1.1.1'
leakCanaryLibVersion = '1.5.4'
okHttpLibVersion = '1.5.0'
okHttpLibVersion = '3.10.0'
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
}
@@ -54,10 +54,10 @@ dependencies {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:bf1c771'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
testImplementation 'org.mockito:mockito-core:2.8.9'
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support:support-v4:$supportLibVersion"
@@ -79,7 +79,7 @@ dependencies {
debugImplementation "com.facebook.stetho:stetho-urlconnection:$stethoLibVersion"
debugImplementation 'com.android.support:multidex:1.0.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
implementation 'io.reactivex.rxjava2:rxjava:2.1.14'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
@@ -93,6 +93,6 @@ dependencies {
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion"
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
debugImplementation "com.facebook.stetho:stetho-okhttp3:$okHttpLibVersion"
implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion"
debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion"
}

View File

@@ -42,3 +42,9 @@
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
}

View File

@@ -61,6 +61,8 @@ import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper;
import static org.schabi.newpipe.extractor.InfoItem.InfoType.PLAYLIST;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@@ -106,7 +108,19 @@ public class MainActivity extends AppCompatActivity {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerClosed(View view) { super.onDrawerClosed(view); }
@Override
public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); }
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0);
}
};
toggle.syncState();
drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@@ -133,13 +147,11 @@ public class MainActivity extends AppCompatActivity {
private boolean changeService(MenuItem item) {
if (item.getGroupId() == R.id.menu_services_group) {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
} else {
if (item.getGroupId() != R.id.menu_services_group)
return false;
}
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
drawer.closeDrawers();
return true;
}
@@ -382,31 +394,45 @@ public class MainActivity extends AppCompatActivity {
}
private void handleIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
try {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(), serviceId, url, title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), serviceId, url, title);
break;
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING);
if (searchString == null) searchString = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(
getSupportFragmentManager(),
serviceId,
searchString);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchQuery = intent.getStringExtra(Constants.KEY_QUERY);
if (searchQuery == null) searchQuery = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(getSupportFragmentManager(), serviceId, searchQuery);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}

View File

@@ -1,14 +1,19 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.FragmentManager;
import android.app.IntentService;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -23,6 +28,7 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
@@ -31,6 +37,8 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
@@ -38,16 +46,19 @@ import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Observer;
import icepick.Icepick;
import icepick.State;
@@ -77,6 +88,8 @@ public class RouterActivity extends AppCompatActivity {
protected String currentUrl;
protected CompositeDisposable disposables = new CompositeDisposable();
private boolean selectionIsDownload = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -104,7 +117,7 @@ public class RouterActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
handleUrl(currentUrl);
}
@@ -165,6 +178,7 @@ public class RouterActivity extends AppCompatActivity {
final String videoPlayerKey = getString(R.string.video_player_key);
final String backgroundPlayerKey = getString(R.string.background_player_key);
final String popupPlayerKey = getString(R.string.popup_player_key);
final String downloadKey = getString(R.string.download_key);
final String alwaysAskKey = getString(R.string.always_ask_open_action_key);
if (selectedChoiceKey.equals(alwaysAskKey)) {
@@ -179,6 +193,8 @@ public class RouterActivity extends AppCompatActivity {
}
} else if (selectedChoiceKey.equals(showInfoKey)) {
handleChoice(showInfoKey);
} else if (selectedChoiceKey.equals(downloadKey)) {
handleChoice(downloadKey);
} else {
final boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
final boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
@@ -236,7 +252,9 @@ public class RouterActivity extends AppCompatActivity {
.setCancelable(true)
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
.setPositiveButton(R.string.always, dialogButtonsClickListener)
.setOnDismissListener((dialog) -> finish())
.setOnDismissListener((dialog) -> {
if(!selectionIsDownload) finish();
})
.create();
//noinspection CodeBlock2Expr
@@ -316,6 +334,9 @@ public class RouterActivity extends AppCompatActivity {
resolveResourceIdFromAttr(context, R.attr.audio)));
}
returnList.add(new AdapterChoiceItem(getString(R.string.download_key), getString(R.string.download),
resolveResourceIdFromAttr(context, R.attr.download)));
return returnList;
}
@@ -347,6 +368,14 @@ public class RouterActivity extends AppCompatActivity {
return;
}
if (selectedChoiceKey.equals(getString(R.string.download_key))) {
if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
selectionIsDownload = true;
openDownloadDialog();
}
return;
}
// stop and bypass FetcherService if InfoScreen was selected since
// StreamDetailFragment can fetch data itself
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
@@ -373,6 +402,47 @@ public class RouterActivity extends AppCompatActivity {
finish();
}
@SuppressLint("CheckResult")
private void openDownloadDialog() {
ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> {
List<VideoStream> sortedVideoStreams = ListHelper.getSortedStreamVideosList(this,
result.getVideoStreams(),
result.getVideoOnlyStreams(),
false);
int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
sortedVideoStreams);
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(result.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.show(fm, "downloadDialog");
fm.executePendingTransactions();
downloadDialog.getDialog().setOnDismissListener(dialog -> {
finish();
});
}, (@NonNull Throwable throwable) -> {
onError();
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (int i: grantResults){
if (i == PackageManager.PERMISSION_DENIED){
finish();
return;
}
}
if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) {
openDownloadDialog();
}
}
private static class AdapterChoiceItem {
final String description, key;
@DrawableRes final int icon;

View File

@@ -128,47 +128,31 @@ public class AboutActivity extends AppCompatActivity {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_about, container, false);
Context context = this.getContext();
TextView version = rootView.findViewById(R.id.app_version);
version.setText(BuildConfig.VERSION_NAME);
View githubLink = rootView.findViewById(R.id.github_link);
githubLink.setOnClickListener(new OnGithubLinkClickListener());
githubLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.github_url), context));
View donationLink = rootView.findViewById(R.id.donation_link);
donationLink.setOnClickListener(new OnDonationLinkClickListener());
donationLink.setOnClickListener(v -> openWebsite(context.getString(R.string.donation_url), context));
View websiteLink = rootView.findViewById(R.id.website_link);
websiteLink.setOnClickListener(new OnWebsiteLinkClickListener());
websiteLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.website_url), context));
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
privacyPolicyLink.setOnClickListener(v -> openWebsite(context.getString(R.string.privacy_policy_url), context));
return rootView;
}
private static class OnGithubLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.github_url)));
context.startActivity(intent);
}
private void openWebsite(String url, Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
}
private static class OnDonationLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.donation_url)));
context.startActivity(intent);
}
}
private static class OnWebsiteLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.website_url)));
context.startActivity(intent);
}
}
}

View File

@@ -1,5 +1,6 @@
package org.schabi.newpipe.about;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -39,7 +40,7 @@ public class LicenseFragment extends Fragment {
* @param license the license to show
*/
public static void showLicense(Context context, License license) {
new LicenseFragmentHelper().execute(context, license);
new LicenseFragmentHelper((Activity) context).execute(license);
}
@Override

View File

@@ -1,8 +1,11 @@
package org.schabi.newpipe.about;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.webkit.WebView;
import org.schabi.newpipe.R;
@@ -10,26 +13,46 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
private Context context;
WeakReference<Activity> weakReference;
private License license;
public LicenseFragmentHelper(@Nullable Activity activity) {
weakReference = new WeakReference<>(activity);
}
@Nullable
private Activity getActivity() {
Activity activity = weakReference.get();
if (activity != null && activity.isFinishing()) {
return null;
} else {
return activity;
}
}
@Override
protected Integer doInBackground(Object... objects) {
context = (Context) objects[0];
license = (License) objects[1];
license = (License) objects[0];
return 1;
}
@Override
protected void onPostExecute(Integer result){
String webViewData = getFormattedLicense(context, license);
AlertDialog.Builder alert = new AlertDialog.Builder(context);
protected void onPostExecute(Integer result) {
Activity activity = getActivity();
if (activity == null) {
return;
}
String webViewData = getFormattedLicense(activity, license);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle(license.getName());
WebView wv = new WebView(context);
WebView wv = new WebView(activity);
wv.loadData(webViewData, "text/html; charset=UTF-8", null);
alert.setView(wv);

View File

@@ -71,6 +71,14 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
info.getUploaderName(), info.getStreamCount());
}
@Ignore
public boolean isIdenticalTo(final PlaylistInfo info) {
return getServiceId() == info.getServiceId() && getName().equals(info.getName()) &&
getStreamCount() == info.getStreamCount() && getUrl().equals(info.getUrl()) &&
getThumbnailUrl().equals(info.getThumbnailUrl()) &&
getUploader().equals(info.getUploaderName());
}
public long getUid() {
return uid;
}

View File

@@ -0,0 +1,158 @@
package org.schabi.newpipe.download;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BaseTransientBottomBar;
import android.support.design.widget.Snackbar;
import android.view.View;
import org.schabi.newpipe.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.get.DownloadMission;
public class DeleteDownloadManager {
private static final String KEY_STATE = "delete_manager_state";
private View mView;
private HashSet<String> mPendingMap;
private List<Disposable> mDisposableList;
private DownloadManager mDownloadManager;
private PublishSubject<DownloadMission> publishSubject = PublishSubject.create();
DeleteDownloadManager(Activity activity) {
mPendingMap = new HashSet<>();
mDisposableList = new ArrayList<>();
mView = activity.findViewById(android.R.id.content);
}
public Observable<DownloadMission> getUndoObservable() {
return publishSubject;
}
public boolean contains(@NonNull DownloadMission mission) {
return mPendingMap.contains(mission.url);
}
public void add(@NonNull DownloadMission mission) {
mPendingMap.add(mission.url);
if (mPendingMap.size() == 1) {
showUndoDeleteSnackbar(mission);
}
}
public void setDownloadManager(@NonNull DownloadManager downloadManager) {
mDownloadManager = downloadManager;
if (mPendingMap.size() < 1) return;
showUndoDeleteSnackbar();
}
public void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) return;
List<String> list = savedInstanceState.getStringArrayList(KEY_STATE);
if (list != null) {
mPendingMap.addAll(list);
}
}
public void saveState(@Nullable Bundle outState) {
if (outState == null) return;
for (Disposable disposable : mDisposableList) {
disposable.dispose();
}
outState.putStringArrayList(KEY_STATE, new ArrayList<>(mPendingMap));
}
private void showUndoDeleteSnackbar() {
if (mPendingMap.size() < 1) return;
String url = mPendingMap.iterator().next();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
DownloadMission mission = mDownloadManager.getMission(i);
if (url.equals(mission.url)) {
showUndoDeleteSnackbar(mission);
break;
}
}
}
private void showUndoDeleteSnackbar(@NonNull DownloadMission mission) {
final Snackbar snackbar = Snackbar.make(mView, mission.name, Snackbar.LENGTH_INDEFINITE);
final Disposable disposable = Observable.timer(3, TimeUnit.SECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(l -> snackbar.dismiss());
mDisposableList.add(disposable);
snackbar.setAction(R.string.undo, v -> {
mPendingMap.remove(mission.url);
publishSubject.onNext(mission);
disposable.dispose();
snackbar.dismiss();
});
snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
if (!disposable.isDisposed()) {
Completable.fromAction(() -> deletePending(mission))
.subscribeOn(Schedulers.io())
.subscribe();
}
mPendingMap.remove(mission.url);
snackbar.removeCallback(this);
mDisposableList.remove(disposable);
showUndoDeleteSnackbar();
}
});
snackbar.show();
}
public void deletePending() {
if (mPendingMap.size() < 1) return;
HashSet<Integer> idSet = new HashSet<>();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
if (contains(mDownloadManager.getMission(i))) {
idSet.add(i);
}
}
for (Integer id : idSet) {
mDownloadManager.deleteMission(id);
}
mPendingMap.clear();
}
private void deletePending(@NonNull DownloadMission mission) {
for (int i = 0; i < mDownloadManager.getCount(); i++) {
if (mission.url.equals(mDownloadManager.getMission(i).url)) {
mDownloadManager.deleteMission(i);
break;
}
}
}
}

View File

@@ -15,12 +15,17 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.SettingsActivity;
import org.schabi.newpipe.util.ThemeHelper;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.AllMissionsFragment;
import us.shandian.giga.ui.fragment.MissionsFragment;
public class DownloadActivity extends AppCompatActivity {
private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag";
private DeleteDownloadManager mDeleteDownloadManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Service
@@ -42,21 +47,35 @@ public class DownloadActivity extends AppCompatActivity {
actionBar.setDisplayShowTitleEnabled(true);
}
// Fragment
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
mDeleteDownloadManager = new DeleteDownloadManager(this);
mDeleteDownloadManager.restoreState(savedInstanceState);
MissionsFragment fragment = (MissionsFragment) getFragmentManager().findFragmentByTag(MISSIONS_FRAGMENT_TAG);
if (fragment != null) {
fragment.setDeleteManager(mDeleteDownloadManager);
} else {
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mDeleteDownloadManager.saveState(outState);
super.onSaveInstanceState(outState);
}
private void updateFragments() {
MissionsFragment fragment = new AllMissionsFragment();
fragment.setDeleteManager(mDeleteDownloadManager);
getFragmentManager().beginTransaction()
.replace(R.id.frame, fragment)
.replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
@@ -80,6 +99,7 @@ public class DownloadActivity extends AppCompatActivity {
case R.id.action_settings: {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
deletePending();
return true;
}
default:
@@ -87,4 +107,15 @@ public class DownloadActivity extends AppCompatActivity {
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
deletePending();
}
private void deletePending() {
Completable.fromAction(mDeleteDownloadManager::deletePending)
.subscribeOn(Schedulers.io())
.subscribe();
}
}

View File

@@ -10,6 +10,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
@@ -127,7 +128,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), "");
try {
NavigationHelper.openSearchFragment(
getFragmentManager(),
ServiceHelper.getSelectedServiceId(activity),
"");
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
}
return super.onOptionsItemSelected(item);
@@ -226,7 +234,9 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
FALLBACK_CHANNEL_URL);
String name = preferences.getString(getString(R.string.main_page_selected_channel_name),
FALLBACK_CHANNEL_NAME);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId,
url,
name);
fragment.useAsFrontPage(true);
return fragment;
} else {
@@ -255,20 +265,13 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
for (final String ks : kl.getAvailableKiosks()) {
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
.setOnMenuItemClickListener(menuItem -> {
try {
NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks);
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
}
});
i++;
}

View File

@@ -17,6 +17,7 @@ import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
@@ -54,14 +55,17 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
@@ -128,7 +132,7 @@ public class VideoDetailFragment
private StreamInfo currentInfo;
private Disposable currentWorker;
private CompositeDisposable disposables = new CompositeDisposable();
@NonNull private CompositeDisposable disposables = new CompositeDisposable();
private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1;
@@ -363,11 +367,15 @@ public class VideoDetailFragment
if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) {
Log.w(TAG, "Can't open channel because we got no channel URL");
} else {
NavigationHelper.openChannelFragment(
getFragmentManager(),
currentInfo.getServiceId(),
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
try {
NavigationHelper.openChannelFragment(
getFragmentManager(),
currentInfo.getServiceId(),
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
break;
case R.id.detail_thumbnail_root_layout:
@@ -540,7 +548,8 @@ public class VideoDetailFragment
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
@@ -557,6 +566,9 @@ public class VideoDetailFragment
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -872,10 +884,7 @@ public class VideoDetailFragment
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
} else {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
audioStream);
startOnExternalPlayer(activity, currentInfo, audioStream);
}
}
@@ -902,10 +911,7 @@ public class VideoDetailFragment
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
selectedVideoStream);
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
openNormalPlayer(selectedVideoStream);
}
@@ -949,6 +955,20 @@ public class VideoDetailFragment
this.autoPlayEnabled = autoplay;
}
private void startOnExternalPlayer(@NonNull final Context context,
@NonNull final StreamInfo info,
@NonNull final Stream selectedStream) {
NavigationHelper.playOnExternalPlayer(context, currentInfo.getName(),
currentInfo.getUploaderName(), selectedStream);
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
disposables.add(recordManager.onViewed(info).onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "Register view failure: ", error)
));
}
@Nullable
private VideoStream getSelectedVideoStream() {
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;

View File

@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -24,6 +25,7 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.StateSaver;
@@ -152,18 +154,35 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ?
getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<PlaylistInfoItem>() {
@Override
public void selected(PlaylistInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(
useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
@@ -178,7 +197,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
private void onStreamSelected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
NavigationHelper.openVideoDetailFragment(useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
@@ -196,7 +217,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -213,6 +235,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}

View File

@@ -8,6 +8,9 @@ import android.view.View;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.util.Constants;
import java.util.Queue;
@@ -166,7 +169,6 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
public void handleResult(@NonNull I result) {
super.handleResult(result);
url = result.getUrl();
name = result.getName();
setTitle(name);

View File

@@ -33,6 +33,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
@@ -161,7 +162,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
@@ -190,6 +192,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -497,7 +502,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}

View File

@@ -11,22 +11,20 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import icepick.State;
import io.reactivex.Single;
@@ -74,11 +72,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
throws ExtractionException {
KioskFragment instance = new KioskFragment();
StreamingService service = NewPipe.getService(serviceId);
UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList()
.getUrlIdHandlerByType(kioskId);
ListLinkHandlerFactory kioskLinkHandlerFactory = service.getKioskList()
.getListLinkHandlerFactoryByType(kioskId);
instance.setInitialData(serviceId,
kioskTypeUrlIdHandler.getUrl(kioskId),
kioskId);
kioskLinkHandlerFactory.fromId(kioskId).getUrl(), kioskId);
instance.kioskId = kioskId;
return instance;
}
@@ -137,7 +134,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload);
return ExtractorHelper.getKioskInfo(serviceId,
url,
contentCountry,
forceReload);
}
@Override
@@ -146,7 +146,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl, contentCountry);
return ExtractorHelper.getMoreKioskItems(serviceId,
url,
currentNextPageUrl,
contentCountry);
}
/*//////////////////////////////////////////////////////////////////////////

View File

@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -19,6 +20,7 @@ import android.widget.TextView;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.App;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
@@ -28,12 +30,14 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -44,6 +48,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@@ -93,7 +98,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
super.onCreate(savedInstanceState);
disposables = new CompositeDisposable();
isBookmarkButtonReady = new AtomicBoolean(false);
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(getContext()));
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(
requireContext()));
}
@Override
@@ -142,6 +148,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -162,6 +169,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -261,11 +271,16 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (!TextUtils.isEmpty(result.getUploaderName())) {
headerUploaderName.setText(result.getUploaderName());
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(v ->
headerUploaderLayout.setOnClickListener(v -> {
try {
NavigationHelper.openChannelFragment(getFragmentManager(),
result.getServiceId(), result.getUploaderUrl(),
result.getUploaderName())
);
result.getServiceId(),
result.getUploaderUrl(),
result.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
});
}
}
@@ -281,14 +296,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
}
remotePlaylistManager.getPlaylist(result)
.flatMap(lists -> getUpdateProcessor(lists, result), (lists, id) -> lists)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistBookmarkSubscriber());
remotePlaylistManager.onUpdate(result)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(integer -> {/* Do nothing*/}, this::onError);
headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view ->
@@ -336,7 +348,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_PLAYLIST,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}
@@ -344,6 +360,17 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
// Utils
//////////////////////////////////////////////////////////////////////////*/
private Flowable<Integer> getUpdateProcessor(@NonNull List<PlaylistRemoteEntity> playlists,
@NonNull PlaylistInfo result) {
final Flowable<Integer> noItemToUpdate = Flowable.just(/*noItemToUpdate=*/-1);
if (playlists.isEmpty()) return noItemToUpdate;
final PlaylistRemoteEntity playlistEntity = playlists.get(0);
if (playlistEntity.isIdenticalTo(result)) return noItemToUpdate;
return remotePlaylistManager.onUpdate(playlists.get(0).getUid(), result).toFlowable();
}
private Subscriber<List<PlaylistRemoteEntity>> getPlaylistBookmarkSubscriber() {
return new Subscriber<List<PlaylistRemoteEntity>>() {
@Override
@@ -416,4 +443,4 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
playlistBookmarkButton.setIcon(ThemeHelper.resolveResourceIdFromAttr(activity, iconAttr));
playlistBookmarkButton.setTitle(titleRes);
}
}
}

View File

@@ -37,26 +37,30 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.LayoutManagerSmoothScroller;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import icepick.State;
@@ -65,14 +69,15 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SearchFragment
extends BaseListFragment<SearchResult, ListExtractor.InfoItemsPage>
extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
implements BackPressable {
/*//////////////////////////////////////////////////////////////////////////
@@ -92,19 +97,29 @@ public class SearchFragment
@State
protected int filterItemCheckedId = -1;
private SearchEngine.Filter filter = SearchEngine.Filter.ANY;
@State
protected int serviceId = Constants.NO_SERVICE_ID;
// this three represet the current search query
@State
protected String searchQuery;
protected String searchString;
@State
protected String lastSearchedQuery;
protected String[] contentFilter;
@State
protected String sortFilter;
// these represtent the last search
@State
protected String lastSearchedString;
@State
protected boolean wasSearchFocused = false;
private int currentPage = 0;
private int currentNextPage = 0;
private Map<Integer, String> menuItemToFilterName;
private StreamingService service;
private String currentPageUrl;
private String nextPageUrl;
private String contentCountry;
private boolean isSuggestionsEnabled = true;
private boolean isSearchHistoryEnabled = true;
@@ -130,11 +145,11 @@ public class SearchFragment
/*////////////////////////////////////////////////////////////////////////*/
public static SearchFragment getInstance(int serviceId, String query) {
public static SearchFragment getInstance(int serviceId, String searchString) {
SearchFragment searchFragment = new SearchFragment();
searchFragment.setQuery(serviceId, query);
searchFragment.setQuery(serviceId, searchString, new String[0], "");
if (!TextUtils.isEmpty(query)) {
if (!TextUtils.isEmpty(searchString)) {
searchFragment.setSearchOnResume();
}
@@ -202,13 +217,22 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onResume() called");
super.onResume();
if (!TextUtils.isEmpty(searchQuery)) {
try {
service = NewPipe.getService(serviceId);
} catch (Exception e) {
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
getActivity().findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"",
"", R.string.general_error));
}
if (!TextUtils.isEmpty(searchString)) {
if (wasLoading.getAndSet(false)) {
if (currentNextPage > currentPage) loadMoreItems();
else search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (infoListAdapter.getItemsList().size() == 0) {
if (savedState == null) {
search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (!isLoading.get() && !wasSearchFocused) {
infoListAdapter.clearStreamItemList();
showEmptyState();
@@ -218,7 +242,7 @@ public class SearchFragment
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) {
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
showKeyboardSearch();
showSuggestionsPanel();
} else {
@@ -247,8 +271,9 @@ public class SearchFragment
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK && !TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
if (resultCode == Activity.RESULT_OK
&& !TextUtils.isEmpty(searchString)) {
search(searchString, contentFilter, sortFilter);
} else Log.e(TAG, "ReCaptcha failed");
break;
@@ -282,20 +307,22 @@ public class SearchFragment
@Override
public void writeTo(Queue<Object> objectsToSave) {
super.writeTo(objectsToSave);
objectsToSave.add(currentPage);
objectsToSave.add(currentNextPage);
objectsToSave.add(currentPageUrl);
objectsToSave.add(nextPageUrl);
}
@Override
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
super.readFrom(savedObjects);
currentPage = (int) savedObjects.poll();
currentNextPage = (int) savedObjects.poll();
currentPageUrl = (String) savedObjects.poll();
nextPageUrl = (String) savedObjects.poll();
}
@Override
public void onSaveInstanceState(Bundle bundle) {
searchQuery = searchEditText != null ? searchEditText.getText().toString() : searchQuery;
searchString = searchEditText != null
? searchEditText.getText().toString()
: searchString;
super.onSaveInstanceState(bundle);
}
@@ -305,8 +332,11 @@ public class SearchFragment
@Override
public void reloadContent() {
if (!TextUtils.isEmpty(searchQuery) || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchQuery) ? searchQuery : searchEditText.getText().toString());
if (!TextUtils.isEmpty(searchString)
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchString)
? searchString
: searchEditText.getText().toString(), new String[0], "");
} else {
if (searchEditText != null) {
searchEditText.setText("");
@@ -330,22 +360,35 @@ public class SearchFragment
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
inflater.inflate(R.menu.menu_search, menu);
menuItemToFilterName = new HashMap<>();
int itemId = 0;
boolean isFirstItem = true;
final Context c = getContext();
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1,
itemId++,
0,
ServiceHelper.getTranslatedFilterString(filter, c));
if(isFirstItem) {
item.setChecked(true);
isFirstItem = false;
}
}
menu.setGroupCheckable(1, true, true);
restoreFilterChecked(menu, filterItemCheckedId);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_filter_all:
case R.id.menu_filter_video:
case R.id.menu_filter_channel:
case R.id.menu_filter_playlist:
changeFilter(item, getFilterFromMenuId(item.getItemId()));
return true;
default:
return super.onOptionsItemSelected(item);
}
List<String> contentFilter = new ArrayList<>(1);
contentFilter.add(menuItemToFilterName.get(item.getItemId()));
changeContentFilter(item, contentFilter);
return true;
}
private void restoreFilterChecked(Menu menu, int itemId) {
@@ -354,21 +397,6 @@ public class SearchFragment
if (item == null) return;
item.setChecked(true);
filter = getFilterFromMenuId(itemId);
}
}
private SearchEngine.Filter getFilterFromMenuId(int itemId) {
switch (itemId) {
case R.id.menu_filter_video:
return SearchEngine.Filter.STREAM;
case R.id.menu_filter_channel:
return SearchEngine.Filter.CHANNEL;
case R.id.menu_filter_playlist:
return SearchEngine.Filter.PLAYLIST;
case R.id.menu_filter_all:
default:
return SearchEngine.Filter.ANY;
}
}
@@ -379,14 +407,21 @@ public class SearchFragment
private TextWatcher textWatcher;
private void showSearchOnStart() {
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery);
searchEditText.setText(searchQuery);
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → "
+ searchString
+ ", lastSearchedQuery → "
+ lastSearchedString);
searchEditText.setText(searchString);
if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) {
if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) {
searchToolbarContainer.setTranslationX(100);
searchToolbarContainer.setAlpha(0f);
searchToolbarContainer.setVisibility(View.VISIBLE);
searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(200).setInterpolator(new DecelerateInterpolator()).start();
searchToolbarContainer.animate()
.translationX(0)
.alpha(1f)
.setDuration(200)
.setInterpolator(new DecelerateInterpolator()).start();
} else {
searchToolbarContainer.setTranslationX(0);
searchToolbarContainer.setAlpha(1f);
@@ -396,47 +431,38 @@ public class SearchFragment
private void initSearchListeners() {
if (DEBUG) Log.d(TAG, "initSearchListeners() called");
searchClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<SuggestionItem>());
showKeyboardSearch();
searchClear.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<>());
showKeyboardSearch();
});
TooltipCompat.setTooltipText(searchClear, getString(R.string.clear));
searchEditText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
});
searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
});
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
@Override
public void onSuggestionItemSelected(SuggestionItem item) {
search(item.query);
search(item.query, new String[0], "");
searchEditText.setText(item.query);
}
@@ -469,21 +495,22 @@ public class SearchFragment
}
};
searchEditText.addTextChangedListener(textWatcher);
searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString());
return true;
}
return false;
}
});
searchEditText.setOnEditorActionListener(
(TextView v, int actionId, KeyEvent event) -> {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if (event != null
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString(), new String[0], "");
return true;
}
return false;
});
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (suggestionDisposable == null || suggestionDisposable.isDisposed())
initSuggestionObserver();
}
private void unsetSearchListeners() {
@@ -513,7 +540,8 @@ public class SearchFragment
if (searchEditText == null) return;
if (searchEditText.requestFocus()) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
}
}
@@ -522,8 +550,10 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called");
if (searchEditText == null) return;
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
searchEditText.clearFocus();
}
@@ -554,10 +584,12 @@ public class SearchFragment
@Override
public boolean onBackPressed() {
if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) {
if (suggestionsPanel.getVisibility() == View.VISIBLE
&& infoListAdapter.getItemsList().size() > 0
&& !isLoading.get()) {
hideSuggestionsPanel();
hideKeyboardSearch();
searchEditText.setText(lastSearchedQuery);
searchEditText.setText(lastSearchedString);
return true;
}
return false;
@@ -573,8 +605,10 @@ public class SearchFragment
final Observable<String> observable = suggestionPublisher
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
.startWith(searchQuery != null ? searchQuery : "")
.filter(query -> isSuggestionsEnabled);
.startWith(searchString != null
? searchString
: "")
.filter(searchString -> isSuggestionsEnabled);
suggestionDisposable = observable
.switchMap(query -> {
@@ -645,56 +679,44 @@ public class SearchFragment
// no-op
}
private void search(final String query) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]");
if (query.isEmpty()) return;
private void search(final String searchString, String[] contentFilter, String sortFilter) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + searchString + "]");
if (searchString.isEmpty()) return;
try {
final StreamingService service = NewPipe.getServiceByUrl(query);
final StreamingService service = NewPipe.getServiceByUrl(searchString);
if (service != null) {
showLoading();
disposables.add(Observable
.fromCallable(new Callable<Intent>() {
@Override
public Intent call() throws Exception {
return NavigationHelper.getIntentByLink(activity, service, query);
}
})
.fromCallable(() ->
NavigationHelper.getIntentByLink(activity, service, searchString))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
showError(getString(R.string.url_not_supported_toast), false);
}
}));
.subscribe(intent -> {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}, throwable ->
showError(getString(R.string.url_not_supported_toast), false)));
return;
}
} catch (Exception e) {
// Exception occurred, it's not a url
}
lastSearchedQuery = query;
searchQuery = query;
currentPage = 0;
lastSearchedString = this.searchString;
this.searchString = searchString;
infoListAdapter.clearStreamItemList();
hideSuggestionsPanel();
hideKeyboardSearch();
historyRecordManager.onSearched(serviceId, query)
historyRecordManager.onSearched(serviceId, searchString)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {},
error -> showSnackBarError(error, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), query, 0)
NewPipe.getNameOfService(serviceId), searchString, 0)
);
suggestionPublisher.onNext(query);
suggestionPublisher.onNext(searchString);
startLoading(false);
}
@@ -703,11 +725,16 @@ public class SearchFragment
super.startLoading(forceLoad);
if (disposables != null) disposables.clear();
if (searchDisposable != null) searchDisposable.dispose();
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, contentCountry, filter)
searchDisposable = ExtractorHelper.searchFor(serviceId,
searchString,
Arrays.asList(contentFilter),
sortFilter,
contentCountry)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
.subscribe(this::handleResult, this::onError);
}
@Override
@@ -715,8 +742,13 @@ public class SearchFragment
isLoading.set(true);
showListFooter(true);
if (searchDisposable != null) searchDisposable.dispose();
currentNextPage = currentPage + 1;
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, contentCountry, filter)
searchDisposable = ExtractorHelper.getMoreSearchItems(
serviceId,
searchString,
asList(contentFilter),
sortFilter,
nextPageUrl,
contentCountry)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
@@ -739,19 +771,22 @@ public class SearchFragment
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void changeFilter(MenuItem item, SearchEngine.Filter filter) {
this.filter = filter;
private void changeContentFilter(MenuItem item, List<String> contentFilter) {
this.filterItemCheckedId = item.getItemId();
item.setChecked(true);
if (!TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
this.contentFilter = new String[] {contentFilter.get(0)};
if (!TextUtils.isEmpty(searchString)) {
search(searchString, this.contentFilter, sortFilter);
}
}
private void setQuery(int serviceId, String searchQuery) {
private void setQuery(int serviceId, String searchString, String[] contentfilter, String sortFilter) {
this.serviceId = serviceId;
this.searchQuery = searchQuery;
this.searchString = searchString;
this.contentFilter = contentfilter;
this.sortFilter = sortFilter;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -772,8 +807,11 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
if (super.onError(exception)) return;
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -798,16 +836,19 @@ public class SearchFragment
//////////////////////////////////////////////////////////////////////////*/
@Override
public void handleResult(@NonNull SearchResult result) {
if (!result.errors.isEmpty()) {
showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0);
public void handleResult(@NonNull SearchInfo result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, 0);
}
lastSearchedQuery = searchQuery;
lastSearchedString = searchString;
nextPageUrl = result.getNextPageUrl();
currentPageUrl = result.getUrl();
if (infoListAdapter.getItemsList().size() == 0) {
if (!result.getResults().isEmpty()) {
infoListAdapter.addInfoItemList(result.getResults());
if (!result.getRelatedItems().isEmpty()) {
infoListAdapter.addInfoItemList(result.getRelatedItems());
} else {
infoListAdapter.clearStreamItemList();
showEmptyState();
@@ -821,12 +862,13 @@ public class SearchFragment
@Override
public void handleNextItems(ListExtractor.InfoItemsPage result) {
showListFooter(false);
currentPage = Integer.parseInt(result.getNextPageUrl());
currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems());
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED, NewPipe.getNameOfService(serviceId)
, "\"" + searchQuery + "\" → page " + currentPage, 0);
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId)
, "\"" + searchString + "\" → page: " + nextPageUrl, 0);
}
super.handleNextItems(result);
}
@@ -835,12 +877,15 @@ public class SearchFragment
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
if (exception instanceof SearchEngine.NothingFoundException) {
if (exception instanceof SearchExtractor.NothingFoundException) {
infoListAdapter.clearStreamItemList();
showEmptyState();
} else {
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
return true;

View File

@@ -17,6 +17,7 @@ import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.OnClickGesture;
import java.util.ArrayList;
@@ -238,7 +239,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return new PlaylistInfoItemHolder(infoItemBuilder, parent);
default:
Log.e(TAG, "Trollolo");
return null;
return new FallbackViewHolder(new View(parent.getContext()));
}
}

View File

@@ -14,6 +14,7 @@ import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.OnClickGesture;
@@ -225,7 +226,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
return new LocalStatisticStreamItemHolder(localItemBuilder, parent);
default:
Log.e(TAG, "No view type has been considered for holder: [" + type + "]");
return null;
return new FallbackViewHolder(new View(parent.getContext()));
}
}

View File

@@ -6,6 +6,7 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -19,9 +20,11 @@ import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
@@ -99,19 +102,26 @@ public final class BookmarkFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
try {
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(),
entry.getUrl(), entry.getName());
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
entry.getUrl(),
entry.getName());
}
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}

View File

@@ -298,6 +298,7 @@ public class StatisticsPlaylistFragment
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -321,6 +322,9 @@ public class StatisticsPlaylistFragment
case 5:
deleteEntry(index);
break;
case 6:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}

View File

@@ -520,7 +520,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.set_as_playlist_thumbnail),
context.getResources().getString(R.string.delete)
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -549,6 +550,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
case 6:
deleteItem(item);
break;
case 7:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}

View File

@@ -40,8 +40,11 @@ public class RemotePlaylistManager {
}).subscribeOn(Schedulers.io());
}
public Single<Integer> onUpdate(final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> playlistRemoteTable.update(new PlaylistRemoteEntity(playlistInfo)))
.subscribeOn(Schedulers.io());
public Single<Integer> onUpdate(final long playlistId, final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> {
PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo);
playlist.setUid(playlistId);
return playlistRemoteTable.update(playlist);
}).subscribeOn(Schedulers.io());
}
}

View File

@@ -15,6 +15,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -38,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
@@ -318,9 +320,15 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});

View File

@@ -26,9 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
@@ -39,17 +37,16 @@ import android.widget.RemoteViews;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -94,7 +91,6 @@ public final class BackgroundPlayer extends Service {
private NotificationCompat.Builder notBuilder;
private RemoteViews notRemoteView;
private RemoteViews bigNotRemoteView;
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
private boolean shouldUpdateOnProgress;
@@ -192,7 +188,9 @@ public final class BackgroundPlayer extends Service {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notRemoteView)
.setCustomBigContentView(bigNotRemoteView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) builder.setPriority(NotificationCompat.PRIORITY_MAX);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
return builder;
}
@@ -249,15 +247,6 @@ public final class BackgroundPlayer extends Service {
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
}
private void setControlsOpacity(@IntRange(from = 0, to = 255) int opacity) {
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@@ -279,8 +268,16 @@ public final class BackgroundPlayer extends Service {
protected class BasePlayerImpl extends BasePlayer {
@NonNull final private AudioPlaybackResolver resolver;
BasePlayerImpl(Context context) {
super(context);
this.resolver = new AudioPlaybackResolver(context, dataSource);
}
@Override
public void initPlayer(boolean playOnReady) {
super.initPlayer(playOnReady);
}
@Override
@@ -293,30 +290,41 @@ public final class BackgroundPlayer extends Service {
startForeground(NOTIFICATION_ID, notBuilder.build());
}
@Override
public void initThumbnail(final String url) {
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
updateNotification(-1);
super.initThumbnail(url);
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
private void updateNotificationThumbnail() {
if (basePlayerImpl == null) return;
if (notRemoteView != null) {
notRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
if (bigNotRemoteView != null) {
bigNotRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
if (loadedImage != null) {
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
updateNotification(-1);
}
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
super.onLoadingFailed(imageUri, view, failReason);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onPrepared(boolean playWhenReady) {
super.onPrepared(playWhenReady);
@@ -390,29 +398,18 @@ public final class BackgroundPlayer extends Service {
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
if (shouldUpdateOnProgress || hasPlayQueueItemChanged) {
resetNotification();
updateNotification(-1);
updateMetadata();
}
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
updateMetadata();
}
@Override
@Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final MediaSource liveSource = super.sourceOf(item, info);
if (liveSource != null) return liveSource;
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index < 0 || index >= info.getAudioStreams().size()) return null;
final AudioStream audio = info.getAudioStreams().get(index);
return buildMediaSource(audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()));
return resolver.resolve(info);
}
@Override
@@ -439,8 +436,8 @@ public final class BackgroundPlayer extends Service {
}
private void updateMetadata() {
if (activityListener != null && currentInfo != null) {
activityListener.onMetadataUpdate(currentInfo);
if (activityListener != null && getCurrentMetadata() != null) {
activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
}
}
@@ -531,44 +528,36 @@ public final class BackgroundPlayer extends Service {
updatePlayback();
}
@Override
public void onBlocked() {
super.onBlocked();
setControlsOpacity(77);
updateNotification(-1);
}
@Override
public void onPlaying() {
super.onPlaying();
setControlsOpacity(255);
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_pause_white);
lockManager.acquireWifiAndCpu();
}
@Override
public void onPaused() {
super.onPaused();
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_play_arrow_white);
lockManager.releaseWifiAndCpu();
}
@Override
public void onCompleted() {
super.onCompleted();
setControlsOpacity(255);
resetNotification();
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
if (notRemoteView != null) {
notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
updateNotificationThumbnail();
updateNotification(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
}
}

View File

@@ -24,16 +24,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
@@ -49,7 +47,6 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Util;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
@@ -57,7 +54,6 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.player.helper.AudioReactor;
import org.schabi.newpipe.player.helper.LoadController;
@@ -72,6 +68,8 @@ import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.SerializedCache;
import java.io.IOException;
@@ -82,12 +80,12 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.SerialDisposable;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
/**
* Base for the players, joining the common properties
@@ -108,17 +106,26 @@ public abstract class BasePlayer implements
@NonNull final protected HistoryRecordManager recordManager;
@NonNull final protected CustomTrackSelector trackSelector;
@NonNull final protected PlayerDataSource dataSource;
@NonNull final private LoadControl loadControl;
@NonNull final private RenderersFactory renderFactory;
@NonNull final private SerialDisposable progressUpdateReactor;
@NonNull final private CompositeDisposable databaseUpdateReactor;
/*//////////////////////////////////////////////////////////////////////////
// Intent
//////////////////////////////////////////////////////////////////////////*/
public static final String REPEAT_MODE = "repeat_mode";
public static final String PLAYBACK_PITCH = "playback_pitch";
public static final String PLAYBACK_SPEED = "playback_speed";
public static final String PLAYBACK_QUALITY = "playback_quality";
public static final String PLAY_QUEUE_KEY = "play_queue_key";
public static final String APPEND_ONLY = "append_only";
public static final String SELECT_ON_APPEND = "select_on_append";
@NonNull public static final String REPEAT_MODE = "repeat_mode";
@NonNull public static final String PLAYBACK_PITCH = "playback_pitch";
@NonNull public static final String PLAYBACK_SPEED = "playback_speed";
@NonNull public static final String PLAYBACK_SKIP_SILENCE = "playback_skip_silence";
@NonNull public static final String PLAYBACK_QUALITY = "playback_quality";
@NonNull public static final String PLAY_QUEUE_KEY = "play_queue_key";
@NonNull public static final String APPEND_ONLY = "append_only";
@NonNull public static final String SELECT_ON_APPEND = "select_on_append";
/*//////////////////////////////////////////////////////////////////////////
// Playback
@@ -129,12 +136,13 @@ public abstract class BasePlayer implements
protected PlayQueue playQueue;
protected PlayQueueAdapter playQueueAdapter;
protected MediaSourceManager playbackManager;
@Nullable protected MediaSourceManager playbackManager;
protected StreamInfo currentInfo;
protected PlayQueueItem currentItem;
@Nullable private PlayQueueItem currentItem;
@Nullable private MediaSourceTag currentMetadata;
@Nullable private Bitmap currentThumbnail;
protected Toast errorToast;
@Nullable protected Toast errorToast;
/*//////////////////////////////////////////////////////////////////////////
// Player
@@ -145,18 +153,11 @@ public abstract class BasePlayer implements
protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds
protected CustomTrackSelector trackSelector;
protected PlayerDataSource dataSource;
protected SimpleExoPlayer simpleExoPlayer;
protected AudioReactor audioReactor;
protected MediaSessionManager mediaSessionManager;
private boolean isPrepared = false;
private boolean isSynchronizing = false;
protected Disposable progressUpdateReactor;
protected CompositeDisposable databaseUpdateReactor;
//////////////////////////////////////////////////////////////////////////*/
@@ -174,29 +175,32 @@ public abstract class BasePlayer implements
context.registerReceiver(broadcastReceiver, intentFilter);
this.recordManager = new HistoryRecordManager(context);
this.progressUpdateReactor = new SerialDisposable();
this.databaseUpdateReactor = new CompositeDisposable();
final String userAgent = Downloader.USER_AGENT;
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
final TrackSelection.Factory trackSelectionFactory =
PlayerHelper.getQualitySelector(context, bandwidthMeter);
this.trackSelector = new CustomTrackSelector(trackSelectionFactory);
this.loadControl = new LoadController(context);
this.renderFactory = new DefaultRenderersFactory(context);
}
public void setup() {
if (simpleExoPlayer == null) initPlayer(/*playOnInit=*/true);
if (simpleExoPlayer == null) {
initPlayer(/*playOnInit=*/true);
}
initListeners();
}
public void initPlayer(final boolean playOnReady) {
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
databaseUpdateReactor = new CompositeDisposable();
final String userAgent = Downloader.USER_AGENT;
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
final TrackSelection.Factory trackSelectionFactory =
PlayerHelper.getQualitySelector(context, bandwidthMeter);
trackSelector = new CustomTrackSelector(trackSelectionFactory);
final LoadControl loadControl = new LoadController(context);
final RenderersFactory renderFactory = new DefaultRenderersFactory(context);
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl);
simpleExoPlayer.addListener(this);
simpleExoPlayer.setPlayWhenReady(playOnReady);
@@ -235,20 +239,24 @@ public abstract class BasePlayer implements
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
final boolean playbackSkipSilence = intent.getBooleanExtra(PLAYBACK_SKIP_SILENCE,
getPlaybackSkipSilence());
// Good to go...
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, /*playOnInit=*/true);
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true);
}
protected void initPlayback(@NonNull final PlayQueue queue,
@Player.RepeatMode final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final boolean playbackSkipSilence,
final boolean playOnReady) {
destroyPlayer();
initPlayer(playOnReady);
setRepeatMode(repeatMode);
setPlaybackParameters(playbackSpeed, playbackPitch);
setPlaybackParameters(playbackSpeed, playbackPitch, playbackSkipSilence);
playQueue = queue;
playQueue.init();
@@ -270,7 +278,7 @@ public abstract class BasePlayer implements
if (playQueue != null) playQueue.dispose();
if (audioReactor != null) audioReactor.dispose();
if (playbackManager != null) playbackManager.dispose();
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
if (mediaSessionManager != null) mediaSessionManager.dispose();
if (playQueueAdapter != null) {
playQueueAdapter.unsetSelectedListener();
@@ -283,20 +291,22 @@ public abstract class BasePlayer implements
destroyPlayer();
unregisterBroadcastReceiver();
trackSelector = null;
databaseUpdateReactor.clear();
progressUpdateReactor.set(null);
simpleExoPlayer = null;
mediaSessionManager = null;
}
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
public void initThumbnail(final String url) {
private void initThumbnail(final String url) {
if (DEBUG) Log.d(TAG, "Thumbnail - initThumbnail() called");
if (url == null || url.isEmpty()) return;
ImageLoader.getInstance().resume();
ImageLoader.getInstance().loadImage(url, this);
ImageLoader.getInstance().loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS,
this);
}
@Override
@@ -309,6 +319,7 @@ public abstract class BasePlayer implements
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
Log.e(TAG, "Thumbnail - onLoadingFailed() called on imageUri = [" + imageUri + "]",
failReason.getCause());
currentThumbnail = null;
}
@Override
@@ -316,64 +327,14 @@ public abstract class BasePlayer implements
if (DEBUG) Log.d(TAG, "Thumbnail - onLoadingComplete() called with: " +
"imageUri = [" + imageUri + "], view = [" + view + "], " +
"loadedImage = [" + loadedImage + "]");
currentThumbnail = loadedImage;
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
if (DEBUG) Log.d(TAG, "Thumbnail - onLoadingCancelled() called with: " +
"imageUri = [" + imageUri + "], view = [" + view + "]");
}
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Building
//////////////////////////////////////////////////////////////////////////*/
public MediaSource buildLiveMediaSource(@NonNull final String sourceUrl,
@C.ContentType final int type) {
if (DEBUG) {
Log.d(TAG, "buildLiveMediaSource() called with: url = [" + sourceUrl +
"], content type = [" + type + "]");
}
if (dataSource == null) return null;
final Uri uri = Uri.parse(sourceUrl);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getLiveDashMediaSourceFactory().createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getLiveHlsMediaSourceFactory().createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
public MediaSource buildMediaSource(@NonNull final String sourceUrl,
@NonNull final String cacheKey,
@NonNull final String overrideExtension) {
if (DEBUG) {
Log.d(TAG, "buildMediaSource() called with: url = [" + sourceUrl +
"], cacheKey = [" + cacheKey + "]" +
"], overrideExtension = [" + overrideExtension + "]");
}
if (dataSource == null) return null;
final Uri uri = Uri.parse(sourceUrl);
@C.ContentType final int type = TextUtils.isEmpty(overrideExtension) ?
Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getDashMediaSourceFactory().createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getHlsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_OTHER:
return dataSource.getExtractorMediaSourceFactory(cacheKey).createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
currentThumbnail = null;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -509,13 +470,11 @@ public abstract class BasePlayer implements
public abstract void onUpdateProgress(int currentProgress, int duration, int bufferPercent);
protected void startProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = getProgressReactor();
progressUpdateReactor.set(getProgressReactor());
}
protected void stopProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = null;
progressUpdateReactor.set(null);
}
public void triggerProgressUpdate() {
@@ -530,7 +489,8 @@ public abstract class BasePlayer implements
private Disposable getProgressReactor() {
return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> triggerProgressUpdate());
.subscribe(ignored -> triggerProgressUpdate(),
error -> Log.e(TAG, "Progress update failure: ", error));
}
/*//////////////////////////////////////////////////////////////////////////
@@ -544,28 +504,16 @@ public abstract class BasePlayer implements
(manifest == null ? "no manifest" : "available manifest") + ", " +
"timeline size = [" + timeline.getWindowCount() + "], " +
"reason = [" + reason + "]");
if (playQueue == null) return;
switch (reason) {
case Player.TIMELINE_CHANGE_REASON_RESET: // called after #block
case Player.TIMELINE_CHANGE_REASON_PREPARED: // called after #unblock
case Player.TIMELINE_CHANGE_REASON_DYNAMIC: // called after playlist changes
// Ensures MediaSourceManager#update is complete
final boolean isPlaylistStable = timeline.getWindowCount() == playQueue.size();
// Ensure dynamic/livestream timeline changes does not cause negative position
if (isPlaylistStable && !isCurrentWindowValid() && !isSynchronizing) {
if (DEBUG) Log.d(TAG, "Playback - negative time position reached, " +
"clamping to default position.");
seekToDefault();
}
break;
}
maybeUpdateCurrentMetadata();
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (DEBUG) Log.d(TAG, "ExoPlayer - onTracksChanged(), " +
"track group size = " + trackGroups.length);
maybeUpdateCurrentMetadata();
}
@Override
@@ -585,6 +533,8 @@ public abstract class BasePlayer implements
} else if (isLoading && !isProgressLoopRunning()) {
startProgressLoop();
}
maybeUpdateCurrentMetadata();
}
@Override
@@ -608,6 +558,7 @@ public abstract class BasePlayer implements
}
break;
case Player.STATE_READY: //3
maybeUpdateCurrentMetadata();
maybeCorrectSeekPosition();
if (!isPrepared) {
isPrepared = true;
@@ -624,38 +575,19 @@ public abstract class BasePlayer implements
}
private void maybeCorrectSeekPosition() {
if (playQueue == null || simpleExoPlayer == null || currentInfo == null) return;
if (playQueue == null || simpleExoPlayer == null || currentMetadata == null) return;
final int currentSourceIndex = playQueue.getIndex();
final PlayQueueItem currentSourceItem = playQueue.getItem();
if (currentSourceItem == null) return;
final long recoveryPositionMillis = currentSourceItem.getRecoveryPosition();
final boolean isCurrentWindowCorrect =
simpleExoPlayer.getCurrentPeriodIndex() == currentSourceIndex;
final StreamInfo currentInfo = currentMetadata.getMetadata();
final long presetStartPositionMillis = currentInfo.getStartPosition() * 1000;
if (recoveryPositionMillis != PlayQueueItem.RECOVERY_UNSET && isCurrentWindowCorrect) {
// Is recovering previous playback?
if (DEBUG) Log.d(TAG, "Playback - Rewinding to recovery time=" +
"[" + getTimeString((int)recoveryPositionMillis) + "]");
seekTo(recoveryPositionMillis);
playQueue.unsetRecovery(currentSourceIndex);
} else if (isSynchronizing && isLive()) {
if (DEBUG) Log.d(TAG, "Playback - Synchronizing livestream to default time");
// Is still synchronizing?
seekToDefault();
} else if (isSynchronizing && presetStartPositionMillis > 0L) {
if (presetStartPositionMillis > 0L) {
// Has another start position?
if (DEBUG) Log.d(TAG, "Playback - Seeking to preset start " +
"position=[" + presetStartPositionMillis + "]");
// Has another start position?
seekTo(presetStartPositionMillis);
currentInfo.setStartPosition(0);
}
isSynchronizing = false;
}
/**
@@ -707,7 +639,7 @@ public abstract class BasePlayer implements
setRecovery();
final Throwable cause = error.getCause();
if (cause instanceof BehindLiveWindowException) {
if (error instanceof BehindLiveWindowException) {
reload();
} else if (cause instanceof UnknownHostException) {
playQueue.error(/*isNetworkProblem=*/true);
@@ -726,22 +658,29 @@ public abstract class BasePlayer implements
public void onPositionDiscontinuity(@Player.DiscontinuityReason final int reason) {
if (DEBUG) Log.d(TAG, "ExoPlayer - onPositionDiscontinuity() called with " +
"reason = [" + reason + "]");
// Refresh the playback if there is a transition to the next video
final int newPeriodIndex = simpleExoPlayer.getCurrentPeriodIndex();
if (playQueue == null) return;
/* Discontinuity reasons!! Thank you ExoPlayer lords */
// Refresh the playback if there is a transition to the next video
final int newWindowIndex = simpleExoPlayer.getCurrentWindowIndex();
switch (reason) {
case DISCONTINUITY_REASON_PERIOD_TRANSITION:
if (newPeriodIndex == playQueue.getIndex()) {
// When player is in single repeat mode and a period transition occurs,
// we need to register a view count here since no metadata has changed
if (getRepeatMode() == Player.REPEAT_MODE_ONE &&
newWindowIndex == playQueue.getIndex()) {
registerView();
} else {
playQueue.offsetIndex(+1);
break;
}
case DISCONTINUITY_REASON_SEEK:
case DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
case DISCONTINUITY_REASON_INTERNAL:
if (playQueue.getIndex() != newWindowIndex) {
playQueue.setIndex(newWindowIndex);
}
break;
}
maybeUpdateCurrentMetadata();
}
@Override
@@ -787,7 +726,7 @@ public abstract class BasePlayer implements
if (DEBUG) Log.d(TAG, "Playback - onPlaybackBlock() called");
currentItem = null;
currentInfo = null;
currentMetadata = null;
simpleExoPlayer.stop();
isPrepared = false;
@@ -804,42 +743,21 @@ public abstract class BasePlayer implements
simpleExoPlayer.prepare(mediaSource);
}
@Override
public void onPlaybackSynchronize(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info) {
public void onPlaybackSynchronize(@NonNull final PlayQueueItem item) {
if (DEBUG) Log.d(TAG, "Playback - onPlaybackSynchronize() called with " +
(info != null ? "available" : "null") + " info, " +
"item=[" + item.getTitle() + "], url=[" + item.getUrl() + "]");
if (simpleExoPlayer == null || playQueue == null) return;
final boolean onPlaybackInitial = currentItem == null;
final boolean hasPlayQueueItemChanged = currentItem != item;
final boolean hasStreamInfoChanged = currentInfo != info;
final int currentPlayQueueIndex = playQueue.indexOf(item);
final int currentPlaylistIndex = simpleExoPlayer.getCurrentWindowIndex();
final int currentPlaylistSize = simpleExoPlayer.getCurrentTimeline().getWindowCount();
// when starting playback on the last item when not repeating, maybe auto queue
if (info != null && currentPlayQueueIndex == playQueue.size() - 1 &&
getRepeatMode() == Player.REPEAT_MODE_OFF &&
PlayerHelper.isAutoQueueEnabled(context)) {
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams());
if (autoQueue != null) playQueue.append(autoQueue.getStreams());
}
// If nothing to synchronize
if (!hasPlayQueueItemChanged && !hasStreamInfoChanged) {
return;
}
if (!hasPlayQueueItemChanged) return;
currentItem = item;
currentInfo = info;
if (hasPlayQueueItemChanged) {
// updates only to the stream info should not trigger another view count
registerView();
initThumbnail(info == null ? item.getThumbnailUrl() : info.getThumbnailUrl());
}
onMetadataChanged(item, info, currentPlayQueueIndex, hasPlayQueueItemChanged);
// Check if on wrong window
if (currentPlayQueueIndex != playQueue.getIndex()) {
@@ -854,39 +772,29 @@ public abstract class BasePlayer implements
"index=[" + currentPlayQueueIndex + "] with " +
"playlist length=[" + currentPlaylistSize + "]");
// If not playing correct stream, change window position and sets flag
// for synchronizing once window position is corrected
// @see maybeCorrectSeekPosition()
} else if (currentPlaylistIndex != currentPlayQueueIndex || onPlaybackInitial ||
!isPlaying()) {
if (DEBUG) Log.d(TAG, "Playback - Rewinding to correct" +
" index=[" + currentPlayQueueIndex + "]," +
" from=[" + currentPlaylistIndex + "], size=[" + currentPlaylistSize + "].");
isSynchronizing = true;
simpleExoPlayer.seekToDefaultPosition(currentPlayQueueIndex);
if (item.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
simpleExoPlayer.seekTo(currentPlayQueueIndex, item.getRecoveryPosition());
playQueue.unsetRecovery(currentPlayQueueIndex);
} else {
simpleExoPlayer.seekToDefaultPosition(currentPlayQueueIndex);
}
}
}
abstract protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged);
@Nullable
@Override
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
final StreamType streamType = info.getStreamType();
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
return null;
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
final StreamInfo info = tag.getMetadata();
if (DEBUG) {
Log.d(TAG, "Playback - onMetadataChanged() called, playing: " + info.getName());
}
if (!info.getHlsUrl().isEmpty()) {
return buildLiveMediaSource(info.getHlsUrl(), C.TYPE_HLS);
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(info.getDashMpdUrl(), C.TYPE_DASH);
}
return null;
initThumbnail(info.getThumbnailUrl());
registerView();
}
@Override
@@ -1019,9 +927,7 @@ public abstract class BasePlayer implements
public void seekTo(long positionMillis) {
if (DEBUG) Log.d(TAG, "seekBy() called with: position = [" + positionMillis + "]");
if (simpleExoPlayer == null || positionMillis < 0 ||
positionMillis > simpleExoPlayer.getDuration()) return;
simpleExoPlayer.seekTo(positionMillis);
if (simpleExoPlayer != null) simpleExoPlayer.seekTo(positionMillis);
}
public void seekBy(long offsetMillis) {
@@ -1045,12 +951,14 @@ public abstract class BasePlayer implements
//////////////////////////////////////////////////////////////////////////*/
private void registerView() {
if (databaseUpdateReactor == null || currentInfo == null) return;
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).onErrorComplete()
if (currentMetadata == null) return;
final StreamInfo currentInfo = currentMetadata.getMetadata();
final Disposable viewRegister = recordManager.onViewed(currentInfo).onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "Player onViewed() failure: ", error)
));
);
databaseUpdateReactor.add(viewRegister);
}
protected void reload() {
@@ -1064,7 +972,7 @@ public abstract class BasePlayer implements
}
protected void savePlaybackState(final StreamInfo info, final long progress) {
if (info == null || databaseUpdateReactor == null) return;
if (info == null) return;
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
.observeOn(AndroidSchedulers.mainThread())
.onErrorComplete()
@@ -1076,7 +984,8 @@ public abstract class BasePlayer implements
}
private void savePlaybackState() {
if (simpleExoPlayer == null || currentInfo == null) return;
if (simpleExoPlayer == null || currentMetadata == null) return;
final StreamInfo currentInfo = currentMetadata.getMetadata();
if (simpleExoPlayer.getCurrentPosition() > RECOVERY_SKIP_THRESHOLD_MILLIS &&
simpleExoPlayer.getCurrentPosition() <
@@ -1084,6 +993,34 @@ public abstract class BasePlayer implements
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
}
}
private void maybeUpdateCurrentMetadata() {
if (simpleExoPlayer == null) return;
final MediaSourceTag metadata;
try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) {
return;
}
if (metadata == null) return;
maybeAutoQueueNextStream(metadata);
if (currentMetadata == metadata) return;
currentMetadata = metadata;
onMetadataChanged(metadata);
}
private void maybeAutoQueueNextStream(@NonNull final MediaSourceTag currentMetadata) {
if (playQueue == null || playQueue.getIndex() != playQueue.size() - 1 ||
getRepeatMode() != Player.REPEAT_MODE_OFF ||
!PlayerHelper.isAutoQueueEnabled(context)) return;
// auto queue when starting playback on the last item when not repeating
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(currentMetadata.getMetadata(),
playQueue.getStreams());
if (autoQueue != null) playQueue.append(autoQueue.getStreams());
}
/*//////////////////////////////////////////////////////////////////////////
// Getters and Setters
//////////////////////////////////////////////////////////////////////////*/
@@ -1100,19 +1037,35 @@ public abstract class BasePlayer implements
return currentState;
}
@Nullable
public MediaSourceTag getCurrentMetadata() {
return currentMetadata;
}
@NonNull
public String getVideoUrl() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUrl();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getUrl();
}
@NonNull
public String getVideoTitle() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getTitle();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getName();
}
@NonNull
public String getUploaderName() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUploader();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getUploaderName();
}
@Nullable
public Bitmap getThumbnail() {
return currentThumbnail == null ?
BitmapFactory.decodeResource(context.getResources(), R.drawable.dummy_thumbnail) :
currentThumbnail;
}
/** Checks if the current playback is a livestream AND is playing at or beyond the live edge */
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isLiveEdge() {
if (simpleExoPlayer == null || !isLive()) return false;
@@ -1146,11 +1099,11 @@ public abstract class BasePlayer implements
@Player.RepeatMode
public int getRepeatMode() {
return simpleExoPlayer.getRepeatMode();
return simpleExoPlayer == null ? Player.REPEAT_MODE_OFF : simpleExoPlayer.getRepeatMode();
}
public void setRepeatMode(@Player.RepeatMode final int repeatMode) {
simpleExoPlayer.setRepeatMode(repeatMode);
if (simpleExoPlayer != null) simpleExoPlayer.setRepeatMode(repeatMode);
}
public float getPlaybackSpeed() {
@@ -1161,19 +1114,22 @@ public abstract class BasePlayer implements
return getPlaybackParameters().pitch;
}
public boolean getPlaybackSkipSilence() {
return getPlaybackParameters().skipSilence;
}
public void setPlaybackSpeed(float speed) {
setPlaybackParameters(speed, getPlaybackPitch());
setPlaybackParameters(speed, getPlaybackPitch(), getPlaybackSkipSilence());
}
public PlaybackParameters getPlaybackParameters() {
final PlaybackParameters defaultParameters = new PlaybackParameters(1f, 1f);
if (simpleExoPlayer == null) return defaultParameters;
if (simpleExoPlayer == null) return PlaybackParameters.DEFAULT;
final PlaybackParameters parameters = simpleExoPlayer.getPlaybackParameters();
return parameters == null ? defaultParameters : parameters;
return parameters == null ? PlaybackParameters.DEFAULT : parameters;
}
public void setPlaybackParameters(float speed, float pitch) {
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch));
public void setPlaybackParameters(float speed, float pitch, boolean skipSilence) {
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch, skipSilence));
}
public PlayQueue getPlayQueue() {
@@ -1189,7 +1145,7 @@ public abstract class BasePlayer implements
}
public boolean isProgressLoopRunning() {
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
return progressUpdateReactor.get() != null;
}
public void setRecovery() {

View File

@@ -25,6 +25,7 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
@@ -57,7 +58,6 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.SubtitleView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
@@ -66,6 +66,8 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
@@ -103,6 +105,7 @@ public final class MainVideoPlayer extends AppCompatActivity
@Nullable private PlayerState playerState;
private boolean isInMultiWindow;
private boolean isBackPressed;
/*//////////////////////////////////////////////////////////////////////////
// Activity LifeCycle
@@ -114,12 +117,17 @@ public final class MainVideoPlayer extends AppCompatActivity
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(this);
ThemeHelper.setTheme(this);
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = PlayerHelper.getScreenBrightness(getApplicationContext());
getWindow().setAttributes(lp);
hideSystemUi();
setContentView(R.layout.activity_main_player);
playerImpl = new VideoPlayerImpl(this);
playerImpl = new VideoPlayerImpl(this);
playerImpl.setup(findViewById(android.R.id.content));
if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) {
@@ -146,7 +154,10 @@ public final class MainVideoPlayer extends AppCompatActivity
protected void onNewIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
super.onNewIntent(intent);
playerImpl.handleIntent(intent);
if (intent != null) {
playerState = null;
playerImpl.handleIntent(intent);
}
}
@Override
@@ -171,7 +182,7 @@ public final class MainVideoPlayer extends AppCompatActivity
playerImpl.setPlaybackQuality(playerState.getPlaybackQuality());
playerImpl.initPlayback(playerState.getPlayQueue(), playerState.getRepeatMode(),
playerState.getPlaybackSpeed(), playerState.getPlaybackPitch(),
playerState.wasPlaying());
playerState.isPlaybackSkipSilence(), playerState.wasPlaying());
}
}
@@ -185,6 +196,12 @@ public final class MainVideoPlayer extends AppCompatActivity
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
isBackPressed = true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (DEBUG) Log.d(TAG, "onSaveInstanceState() called");
@@ -194,7 +211,8 @@ public final class MainVideoPlayer extends AppCompatActivity
playerImpl.setRecovery();
playerState = new PlayerState(playerImpl.getPlayQueue(), playerImpl.getRepeatMode(),
playerImpl.getPlaybackSpeed(), playerImpl.getPlaybackPitch(),
playerImpl.getPlaybackQuality(), playerImpl.isPlaying());
playerImpl.getPlaybackQuality(), playerImpl.getPlaybackSkipSilence(),
playerImpl.isPlaying());
StateSaver.tryToSave(isChangingConfigurations(), null, outState, this);
}
@@ -202,7 +220,17 @@ public final class MainVideoPlayer extends AppCompatActivity
protected void onStop() {
if (DEBUG) Log.d(TAG, "onStop() called");
super.onStop();
PlayerHelper.setScreenBrightness(getApplicationContext(),
getWindow().getAttributes().screenBrightness);
if (playerImpl == null) return;
if (!isBackPressed) {
playerImpl.minimize();
}
playerImpl.destroy();
isInMultiWindow = false;
isBackPressed = false;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -326,8 +354,11 @@ public final class MainVideoPlayer extends AppCompatActivity
////////////////////////////////////////////////////////////////////////////
@Override
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch) {
if (playerImpl != null) playerImpl.setPlaybackParameters(playbackTempo, playbackPitch);
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
boolean playbackSkipSilence) {
if (playerImpl != null) {
playerImpl.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
}
}
///////////////////////////////////////////////////////////////////////////
@@ -432,6 +463,21 @@ public final class MainVideoPlayer extends AppCompatActivity
switchPopupButton.setOnClickListener(this);
}
public void minimize() {
switch (PlayerHelper.getMinimizeOnExitAction(context)) {
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND:
onPlayBackgroundButtonClicked();
break;
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP:
onFullScreenButtonClicked();
break;
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE:
default:
// No action
break;
}
}
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@@ -452,14 +498,11 @@ public final class MainVideoPlayer extends AppCompatActivity
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
titleTextView.setText(getVideoTitle());
channelTextView.setText(getUploaderName());
titleTextView.setText(tag.getMetadata().getName());
channelTextView.setText(tag.getMetadata().getUploaderName());
}
@Override
@@ -492,6 +535,7 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
context.startService(intent);
@@ -513,6 +557,7 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
context.startService(intent);
@@ -608,7 +653,8 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public void onPlaybackSpeedClicked() {
PlaybackParameterDialog.newInstance(getPlaybackSpeed(), getPlaybackPitch())
PlaybackParameterDialog
.newInstance(getPlaybackSpeed(), getPlaybackPitch(), getPlaybackSkipSilence())
.show(getSupportFragmentManager(), TAG);
}
@@ -638,14 +684,19 @@ public final class MainVideoPlayer extends AppCompatActivity
}
@Override
protected int getDefaultResolutionIndex(final List<VideoStream> sortedVideos) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
}
protected VideoPlaybackResolver.QualityResolver getQualityResolver() {
return new VideoPlaybackResolver.QualityResolver() {
@Override
public int getDefaultResolutionIndex(List<VideoStream> sortedVideos) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos, playbackQuality);
@Override
public int getOverrideResolutionIndex(List<VideoStream> sortedVideos,
String playbackQuality) {
return ListHelper.getResolutionIndex(context, sortedVideos, playbackQuality);
}
};
}
/*//////////////////////////////////////////////////////////////////////////
@@ -669,7 +720,6 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public void onBuffering() {
super.onBuffering();
animatePlayButtons(false, 100);
getRootView().setKeepScreenOn(true);
}
@@ -845,7 +895,6 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
if (!playerImpl.isPlaying()) return false;
if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
playerImpl.onFastForward();
@@ -882,7 +931,9 @@ public final class MainVideoPlayer extends AppCompatActivity
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
private float currentBrightness = .5f;
private float currentBrightness = getWindow().getAttributes().screenBrightness > 0
? getWindow().getAttributes().screenBrightness
: 0.5f;
private int currentVolume, maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;

View File

@@ -14,21 +14,26 @@ public class PlayerState implements Serializable {
private final float playbackSpeed;
private final float playbackPitch;
@Nullable private final String playbackQuality;
private final boolean playbackSkipSilence;
private final boolean wasPlaying;
PlayerState(@NonNull final PlayQueue playQueue, final int repeatMode,
final float playbackSpeed, final float playbackPitch, final boolean wasPlaying) {
this(playQueue, repeatMode, playbackSpeed, playbackPitch, null, wasPlaying);
final float playbackSpeed, final float playbackPitch,
final boolean playbackSkipSilence, final boolean wasPlaying) {
this(playQueue, repeatMode, playbackSpeed, playbackPitch, null,
playbackSkipSilence, wasPlaying);
}
PlayerState(@NonNull final PlayQueue playQueue, final int repeatMode,
final float playbackSpeed, final float playbackPitch,
@Nullable final String playbackQuality, final boolean wasPlaying) {
@Nullable final String playbackQuality, final boolean playbackSkipSilence,
final boolean wasPlaying) {
this.playQueue = playQueue;
this.repeatMode = repeatMode;
this.playbackSpeed = playbackSpeed;
this.playbackPitch = playbackPitch;
this.playbackQuality = playbackQuality;
this.playbackSkipSilence = playbackSkipSilence;
this.wasPlaying = wasPlaying;
}
@@ -62,6 +67,10 @@ public class PlayerState implements Serializable {
return playbackQuality;
}
public boolean isPlaybackSkipSilence() {
return playbackSkipSilence;
}
public boolean wasPlaying() {
return wasPlaying;
}

View File

@@ -34,7 +34,6 @@ import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -56,16 +55,17 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.SubtitleView;
import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -98,6 +98,11 @@ public final class PopupVideoPlayer extends Service {
private static final int MINIMUM_SHOW_EXTRA_WIDTH_DP = 300;
private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams;
private GestureDetector gestureDetector;
@@ -191,14 +196,17 @@ public final class PopupVideoPlayer extends Service {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
layoutParamType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
@@ -228,6 +236,7 @@ public final class PopupVideoPlayer extends Service {
notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName());
notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail());
notRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause,
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
@@ -243,11 +252,15 @@ public final class PopupVideoPlayer extends Service {
setRepeatModeRemote(notRemoteView, playerImpl.getRepeatMode());
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContent(notRemoteView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
return builder;
}
/**
@@ -366,6 +379,12 @@ public final class PopupVideoPlayer extends Service {
}
}
private void updateWindowFlags(final int flags) {
if (windowLayoutParams == null || windowManager == null || playerImpl == null) return;
windowLayoutParams.flags = flags;
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
}
///////////////////////////////////////////////////////////////////////////
protected class VideoPlayerImpl extends VideoPlayer implements View.OnLayoutChangeListener {
@@ -428,21 +447,6 @@ public final class PopupVideoPlayer extends Service {
super.destroy();
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
if (loadedImage != null) {
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
notBuilder = createNotification();
if (notRemoteView != null) {
notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
}
updateNotification(-1);
}
}
@Override
public void onFullScreenButtonClicked() {
super.onFullScreenButtonClicked();
@@ -459,6 +463,7 @@ public final class PopupVideoPlayer extends Service {
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -510,14 +515,47 @@ public final class PopupVideoPlayer extends Service {
}
@Override
protected int getDefaultResolutionIndex(final List<VideoStream> sortedVideos) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
protected VideoPlaybackResolver.QualityResolver getQualityResolver() {
return new VideoPlaybackResolver.QualityResolver() {
@Override
public int getDefaultResolutionIndex(List<VideoStream> sortedVideos) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
}
@Override
public int getOverrideResolutionIndex(List<VideoStream> sortedVideos,
String playbackQuality) {
return ListHelper.getPopupResolutionIndex(context, sortedVideos,
playbackQuality);
}
};
}
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
// rebuild notification here since remote view does not release bitmaps,
// causing memory leaks
resetNotification();
updateNotification(-1);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos, playbackQuality);
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
super.onLoadingFailed(imageUri, view, failReason);
resetNotification();
updateNotification(-1);
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
super.onLoadingCancelled(imageUri, view);
resetNotification();
updateNotification(-1);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -538,8 +576,8 @@ public final class PopupVideoPlayer extends Service {
}
private void updateMetadata() {
if (activityListener != null && currentInfo != null) {
activityListener.onMetadataUpdate(currentInfo);
if (activityListener != null && getCurrentMetadata() != null) {
activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
}
}
@@ -571,8 +609,9 @@ public final class PopupVideoPlayer extends Service {
public void onRepeatModeChanged(int i) {
super.onRepeatModeChanged(i);
setRepeatModeRemote(notRemoteView, i);
updateNotification(-1);
updatePlayback();
resetNotification();
updateNotification(-1);
}
@Override
@@ -585,11 +624,10 @@ public final class PopupVideoPlayer extends Service {
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
resetNotification();
updateNotification(-1);
updateMetadata();
}
@@ -652,46 +690,70 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onBlocked() {
super.onBlocked();
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
}
@Override
public void onPlaying() {
super.onPlaying();
updateNotification(R.drawable.ic_pause_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
lockManager.acquireWifiAndCpu();
updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_pause_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
startForeground(NOTIFICATION_ID, notBuilder.build());
lockManager.acquireWifiAndCpu();
}
@Override
public void onBuffering() {
super.onBuffering();
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
}
@Override
public void onPaused() {
super.onPaused();
updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white);
lockManager.releaseWifiAndCpu();
stopForeground(false);
}
@Override
public void onPausedSeek() {
super.onPausedSeek();
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
}
@Override
public void onCompleted() {
super.onCompleted();
updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_replay_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
stopForeground(false);
}
@Override
@@ -709,16 +771,15 @@ public final class PopupVideoPlayer extends Service {
super.hideControlsAndButton(duration, delay, videoPlayPause);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
/*package-private*/ void enableVideoRenderer(final boolean enable) {
final int videoRendererIndex = getRendererIndex(C.TRACK_TYPE_VIDEO);
if (trackSelector != null && videoRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setRendererDisabled(videoRendererIndex, !enable);
if (videoRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(videoRendererIndex, !enable));
}
}

View File

@@ -187,6 +187,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
this.player.getRepeatMode(),
this.player.getPlaybackSpeed(),
this.player.getPlaybackPitch(),
this.player.getPlaybackSkipSilence(),
null
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
@@ -340,6 +341,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
return true;
});
final MenuItem share = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, /*pos=*/3,
Menu.NONE, R.string.share);
share.setOnMenuItemClickListener(menuItem -> {
shareUrl(item.getTitle(), item.getUrl());
return true;
});
menu.show();
}
@@ -459,13 +467,16 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
private void openPlaybackParameterDialog() {
if (player == null) return;
PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(),
player.getPlaybackPitch()).show(getSupportFragmentManager(), getTag());
PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(),
player.getPlaybackSkipSilence()).show(getSupportFragmentManager(), getTag());
}
@Override
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch) {
if (player != null) player.setPlaybackParameters(playbackTempo, playbackPitch);
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
boolean playbackSkipSilence) {
if (player != null) {
player.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
}
}
////////////////////////////////////////////////////////////////////////////
@@ -509,6 +520,18 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
.show(getSupportFragmentManager(), getTag());
}
////////////////////////////////////////////////////////////////////////////
// Share
////////////////////////////////////////////////////////////////////////////
private void shareUrl(String subject, String url) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, url);
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
}
////////////////////////////////////////////////////////////////////////////
// Binding Service Listener
////////////////////////////////////////////////////////////////////////////

View File

@@ -29,7 +29,6 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
@@ -47,11 +46,9 @@ import android.widget.SeekBar;
import android.widget.TextView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
@@ -62,21 +59,17 @@ import com.google.android.exoplayer2.video.VideoListener;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.Subtitles;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.exoplayer2.C.SELECTION_FLAG_AUTOSELECT;
import static com.google.android.exoplayer2.C.TIME_UNSET;
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -105,13 +98,12 @@ public abstract class VideoPlayer extends BasePlayer
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
private ArrayList<VideoStream> availableStreams;
private List<VideoStream> availableStreams;
private int selectedStreamIndex;
protected String playbackQuality;
protected boolean wasPlaying = false;
@NonNull final private VideoPlaybackResolver resolver;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
@@ -162,6 +154,7 @@ public abstract class VideoPlayer extends BasePlayer
public VideoPlayer(String debugTag, Context context) {
super(context);
this.TAG = debugTag;
this.resolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
}
public void setup(View rootView) {
@@ -241,7 +234,8 @@ public abstract class VideoPlayer extends BasePlayer
// Setup audio session with onboard equalizer
if (Build.VERSION.SDK_INT >= 21) {
trackSelector.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context));
trackSelector.setParameters(trackSelector.buildUponParameters()
.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context)));
}
}
@@ -297,8 +291,9 @@ public abstract class VideoPlayer extends BasePlayer
0, Menu.NONE, R.string.caption_none);
captionOffItem.setOnMenuItemClickListener(menuItem -> {
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setRendererDisabled(textRendererIndex, true);
if (textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(textRendererIndex, true));
}
return true;
});
@@ -310,68 +305,61 @@ public abstract class VideoPlayer extends BasePlayer
i + 1, Menu.NONE, captionLanguage);
captionItem.setOnMenuItemClickListener(menuItem -> {
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) {
if (textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setPreferredTextLanguage(captionLanguage);
trackSelector.setRendererDisabled(textRendererIndex, false);
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(textRendererIndex, false));
}
return true;
});
}
captionPopupMenu.setOnDismissListener(this);
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected abstract int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
private void updateStreamRelatedViews() {
if (getCurrentMetadata() == null) return;
final MediaSourceTag tag = getCurrentMetadata();
final StreamInfo metadata = tag.getMetadata();
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
qualityTextView.setVisibility(View.GONE);
playbackSpeedTextView.setVisibility(View.GONE);
playbackEndTime.setVisibility(View.GONE);
playbackLiveSync.setVisibility(View.GONE);
final StreamType streamType = info == null ? StreamType.NONE : info.getStreamType();
switch (streamType) {
switch (metadata.getStreamType()) {
case AUDIO_STREAM:
surfaceView.setVisibility(View.GONE);
endScreen.setVisibility(View.VISIBLE);
playbackEndTime.setVisibility(View.VISIBLE);
break;
case AUDIO_LIVE_STREAM:
surfaceView.setVisibility(View.GONE);
endScreen.setVisibility(View.VISIBLE);
playbackLiveSync.setVisibility(View.VISIBLE);
break;
case LIVE_STREAM:
surfaceView.setVisibility(View.VISIBLE);
endScreen.setVisibility(View.GONE);
playbackLiveSync.setVisibility(View.VISIBLE);
break;
case VIDEO_STREAM:
if (info.getVideoStreams().size() + info.getVideoOnlyStreams().size() == 0) break;
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
availableStreams = new ArrayList<>(videos);
if (playbackQuality == null) {
selectedStreamIndex = getDefaultResolutionIndex(videos);
} else {
selectedStreamIndex = getOverrideResolutionIndex(videos, getPlaybackQuality());
}
if (metadata.getVideoStreams().size() + metadata.getVideoOnlyStreams().size() == 0)
break;
availableStreams = tag.getSortedAvailableVideoStreams();
selectedStreamIndex = tag.getSelectedVideoStreamIndex();
buildQualityMenu();
qualityTextView.setVisibility(View.VISIBLE);
qualityTextView.setVisibility(View.VISIBLE);
surfaceView.setVisibility(View.VISIBLE);
default:
endScreen.setVisibility(View.GONE);
playbackEndTime.setVisibility(View.VISIBLE);
break;
}
@@ -379,69 +367,21 @@ public abstract class VideoPlayer extends BasePlayer
buildPlaybackSpeedMenu();
playbackSpeedTextView.setVisibility(View.VISIBLE);
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver();
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
updateStreamRelatedViews();
}
@Override
@Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final MediaSource liveSource = super.sourceOf(item, info);
if (liveSource != null) return liveSource;
List<MediaSource> mediaSources = new ArrayList<>();
// Create video stream source
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
final int index;
if (videos.isEmpty()) {
index = -1;
} else if (playbackQuality == null) {
index = getDefaultResolutionIndex(videos);
} else {
index = getOverrideResolutionIndex(videos, getPlaybackQuality());
}
final VideoStream video = index >= 0 && index < videos.size() ? videos.get(index) : null;
if (video != null) {
final MediaSource streamSource = buildMediaSource(video.getUrl(),
PlayerHelper.cacheKeyOf(info, video),
MediaFormat.getSuffixById(video.getFormatId()));
mediaSources.add(streamSource);
}
// Create optional audio stream source
final List<AudioStream> audioStreams = info.getAudioStreams();
final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
ListHelper.getDefaultAudioFormat(context, audioStreams));
// Use the audio stream if there is no video stream, or
// Merge with audio stream in case if video does not contain audio
if (audio != null && ((video != null && video.isVideoOnly) || video == null)) {
final MediaSource audioSource = buildMediaSource(audio.getUrl(),
PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()));
mediaSources.add(audioSource);
}
// If there is no audio or video sources, then this media source cannot be played back
if (mediaSources.isEmpty()) return null;
// Below are auxiliary media sources
// Create subtitle sources
for (final Subtitles subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFileType());
if (mimeType == null) continue;
final Format textFormat = Format.createTextSampleFormat(null, mimeType,
SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle));
final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
.createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
mediaSources.add(textSource);
}
if (mediaSources.size() == 1) {
return mediaSources.get(0);
} else {
return new MergingMediaSource(mediaSources.toArray(
new MediaSource[mediaSources.size()]));
}
return resolver.resolve(info);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -460,7 +400,6 @@ public abstract class VideoPlayer extends BasePlayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
animateView(endScreen, false, 0);
loadingPanel.setBackgroundColor(Color.BLACK);
animateView(loadingPanel, true, 0);
animateView(surfaceForeground, true, 100);
@@ -470,6 +409,8 @@ public abstract class VideoPlayer extends BasePlayer
public void onPlaying() {
super.onPlaying();
updateStreamRelatedViews();
showAndAnimateControl(-1, true);
playbackSeekBar.setEnabled(true);
@@ -480,14 +421,12 @@ public abstract class VideoPlayer extends BasePlayer
loadingPanel.setVisibility(View.GONE);
animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
animateView(endScreen, false, 0);
}
@Override
public void onBuffering() {
if (DEBUG) Log.d(TAG, "onBuffering() called");
loadingPanel.setBackgroundColor(Color.TRANSPARENT);
animateView(loadingPanel, true, 500);
}
@Override
@@ -552,8 +491,7 @@ public abstract class VideoPlayer extends BasePlayer
final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT);
if (captionTextView == null) return;
if (trackSelector == null || trackSelector.getCurrentMappedTrackInfo() == null ||
textRenderer == RENDERER_UNAVAILABLE) {
if (trackSelector.getCurrentMappedTrackInfo() == null || textRenderer == RENDERER_UNAVAILABLE) {
captionTextView.setVisibility(View.GONE);
return;
}
@@ -575,8 +513,8 @@ public abstract class VideoPlayer extends BasePlayer
// Build UI
buildCaptionMenu(availableLanguages);
if (trackSelector.getRendererDisabled(textRenderer) || preferredLanguage == null ||
!availableLanguages.contains(preferredLanguage)) {
if (trackSelector.getParameters().getRendererDisabled(textRenderer) ||
preferredLanguage == null || !availableLanguages.contains(preferredLanguage)) {
captionTextView.setText(R.string.caption_none);
} else {
captionTextView.setText(preferredLanguage);
@@ -905,11 +843,12 @@ public abstract class VideoPlayer extends BasePlayer
//////////////////////////////////////////////////////////////////////////*/
public void setPlaybackQuality(final String quality) {
this.playbackQuality = quality;
this.resolver.setPlaybackQuality(quality);
}
@Nullable
public String getPlaybackQuality() {
return playbackQuality;
return resolver.getPlaybackQuality();
}
public AspectRatioFrameLayout getAspectRatioFrameLayout() {

View File

@@ -11,7 +11,6 @@ import android.view.KeyEvent;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import org.schabi.newpipe.player.mediasession.DummyPlaybackPreparer;
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
@@ -26,10 +25,12 @@ public class MediaSessionManager {
@NonNull final Player player,
@NonNull final MediaSessionCallback callback) {
this.mediaSession = new MediaSessionCompat(context, TAG);
this.mediaSession.setActive(true);
this.sessionConnector = new MediaSessionConnector(mediaSession,
new PlayQueuePlaybackController(callback));
this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback));
this.sessionConnector.setPlayer(player, new DummyPlaybackPreparer());
this.sessionConnector.setPlayer(player, null);
}
@Nullable
@@ -37,4 +38,14 @@ public class MediaSessionManager {
public KeyEvent handleMediaButtonIntent(final Intent intent) {
return MediaButtonReceiver.handleIntent(mediaSession, intent);
}
/**
* Should be called on player destruction to prevent leakage.
* */
public void dispose() {
this.sessionConnector.setPlayer(null, null);
this.sessionConnector.setQueueNavigator(null);
this.mediaSession.setActive(false);
this.mediaSession.release();
}
}

View File

@@ -21,25 +21,34 @@ import static org.schabi.newpipe.player.BasePlayer.DEBUG;
public class PlaybackParameterDialog extends DialogFragment {
@NonNull private static final String TAG = "PlaybackParameterDialog";
public static final double MINIMUM_PLAYBACK_VALUE = 0.25f;
// Minimum allowable range in ExoPlayer
public static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
public static final double MAXIMUM_PLAYBACK_VALUE = 3.00f;
public static final char STEP_UP_SIGN = '+';
public static final char STEP_DOWN_SIGN = '-';
public static final double PLAYBACK_STEP_VALUE = 0.05f;
public static final double NIGHTCORE_TEMPO = 1.20f;
public static final double NIGHTCORE_PITCH_LOWER = 1.15f;
public static final double NIGHTCORE_PITCH_UPPER = 1.25f;
public static final double STEP_ONE_PERCENT_VALUE = 0.01f;
public static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
public static final double STEP_TEN_PERCENT_VALUE = 0.10f;
public static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f;
public static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f;
public static final double DEFAULT_TEMPO = 1.00f;
public static final double DEFAULT_PITCH = 1.00f;
public static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
public static final boolean DEFAULT_SKIP_SILENCE = false;
@NonNull private static final String INITIAL_TEMPO_KEY = "initial_tempo_key";
@NonNull private static final String INITIAL_PITCH_KEY = "initial_pitch_key";
@NonNull private static final String TEMPO_KEY = "tempo_key";
@NonNull private static final String PITCH_KEY = "pitch_key";
@NonNull private static final String STEP_SIZE_KEY = "step_size_key";
public interface Callback {
void onPlaybackParameterChanged(final float playbackTempo, final float playbackPitch);
void onPlaybackParameterChanged(final float playbackTempo, final float playbackPitch,
final boolean playbackSkipSilence);
}
@Nullable private Callback callback;
@@ -50,6 +59,11 @@ public class PlaybackParameterDialog extends DialogFragment {
private double initialTempo = DEFAULT_TEMPO;
private double initialPitch = DEFAULT_PITCH;
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
private double tempo = DEFAULT_TEMPO;
private double pitch = DEFAULT_PITCH;
private double stepSize = DEFAULT_STEP;
@Nullable private SeekBar tempoSlider;
@Nullable private TextView tempoMinimumText;
@@ -65,16 +79,26 @@ public class PlaybackParameterDialog extends DialogFragment {
@Nullable private TextView pitchStepDownText;
@Nullable private TextView pitchStepUpText;
@Nullable private CheckBox unhookingCheckbox;
@Nullable private TextView stepSizeOnePercentText;
@Nullable private TextView stepSizeFivePercentText;
@Nullable private TextView stepSizeTenPercentText;
@Nullable private TextView stepSizeTwentyFivePercentText;
@Nullable private TextView stepSizeOneHundredPercentText;
@Nullable private TextView nightCorePresetText;
@Nullable private TextView resetPresetText;
@Nullable private CheckBox unhookingCheckbox;
@Nullable private CheckBox skipSilenceCheckbox;
public static PlaybackParameterDialog newInstance(final double playbackTempo,
final double playbackPitch) {
final double playbackPitch,
final boolean playbackSkipSilence) {
PlaybackParameterDialog dialog = new PlaybackParameterDialog();
dialog.initialTempo = playbackTempo;
dialog.initialPitch = playbackPitch;
dialog.tempo = playbackTempo;
dialog.pitch = playbackPitch;
dialog.initialSkipSilence = playbackSkipSilence;
return dialog;
}
@@ -98,6 +122,10 @@ public class PlaybackParameterDialog extends DialogFragment {
if (savedInstanceState != null) {
initialTempo = savedInstanceState.getDouble(INITIAL_TEMPO_KEY, DEFAULT_TEMPO);
initialPitch = savedInstanceState.getDouble(INITIAL_PITCH_KEY, DEFAULT_PITCH);
tempo = savedInstanceState.getDouble(TEMPO_KEY, DEFAULT_TEMPO);
pitch = savedInstanceState.getDouble(PITCH_KEY, DEFAULT_PITCH);
stepSize = savedInstanceState.getDouble(STEP_SIZE_KEY, DEFAULT_STEP);
}
}
@@ -106,6 +134,10 @@ public class PlaybackParameterDialog extends DialogFragment {
super.onSaveInstanceState(outState);
outState.putDouble(INITIAL_TEMPO_KEY, initialTempo);
outState.putDouble(INITIAL_PITCH_KEY, initialPitch);
outState.putDouble(TEMPO_KEY, getCurrentTempo());
outState.putDouble(PITCH_KEY, getCurrentPitch());
outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize());
}
/*//////////////////////////////////////////////////////////////////////////
@@ -123,7 +155,9 @@ public class PlaybackParameterDialog extends DialogFragment {
.setView(view)
.setCancelable(true)
.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
setPlaybackParameters(initialTempo, initialPitch))
setPlaybackParameters(initialTempo, initialPitch, initialSkipSilence))
.setNeutralButton(R.string.playback_reset, (dialogInterface, i) ->
setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH, DEFAULT_SKIP_SILENCE))
.setPositiveButton(R.string.finish, (dialogInterface, i) ->
setCurrentPlaybackParameters());
@@ -136,9 +170,13 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupControlViews(@NonNull View rootView) {
setupHookingControl(rootView);
setupSkipSilenceControl(rootView);
setupTempoControl(rootView);
setupPitchControl(rootView);
setupPresetControl(rootView);
changeStepSize(stepSize);
setupStepSizeSelector(rootView);
}
private void setupTempoControl(@NonNull View rootView) {
@@ -150,31 +188,15 @@ public class PlaybackParameterDialog extends DialogFragment {
tempoStepDownText = rootView.findViewById(R.id.tempoStepDown);
if (tempoCurrentText != null)
tempoCurrentText.setText(PlayerHelper.formatSpeed(initialTempo));
tempoCurrentText.setText(PlayerHelper.formatSpeed(tempo));
if (tempoMaximumText != null)
tempoMaximumText.setText(PlayerHelper.formatSpeed(MAXIMUM_PLAYBACK_VALUE));
if (tempoMinimumText != null)
tempoMinimumText.setText(PlayerHelper.formatSpeed(MINIMUM_PLAYBACK_VALUE));
if (tempoStepUpText != null) {
tempoStepUpText.setText(getStepUpPercentString(PLAYBACK_STEP_VALUE));
tempoStepUpText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() + PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (tempoStepDownText != null) {
tempoStepDownText.setText(getStepDownPercentString(PLAYBACK_STEP_VALUE));
tempoStepDownText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() - PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (tempoSlider != null) {
tempoSlider.setMax(strategy.progressOf(MAXIMUM_PLAYBACK_VALUE));
tempoSlider.setProgress(strategy.progressOf(initialTempo));
tempoSlider.setProgress(strategy.progressOf(tempo));
tempoSlider.setOnSeekBarChangeListener(getOnTempoChangedListener());
}
}
@@ -188,31 +210,15 @@ public class PlaybackParameterDialog extends DialogFragment {
pitchStepUpText = rootView.findViewById(R.id.pitchStepUp);
if (pitchCurrentText != null)
pitchCurrentText.setText(PlayerHelper.formatPitch(initialPitch));
pitchCurrentText.setText(PlayerHelper.formatPitch(pitch));
if (pitchMaximumText != null)
pitchMaximumText.setText(PlayerHelper.formatPitch(MAXIMUM_PLAYBACK_VALUE));
if (pitchMinimumText != null)
pitchMinimumText.setText(PlayerHelper.formatPitch(MINIMUM_PLAYBACK_VALUE));
if (pitchStepUpText != null) {
pitchStepUpText.setText(getStepUpPercentString(PLAYBACK_STEP_VALUE));
pitchStepUpText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() + PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (pitchStepDownText != null) {
pitchStepDownText.setText(getStepDownPercentString(PLAYBACK_STEP_VALUE));
pitchStepDownText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() - PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (pitchSlider != null) {
pitchSlider.setMax(strategy.progressOf(MAXIMUM_PLAYBACK_VALUE));
pitchSlider.setProgress(strategy.progressOf(initialPitch));
pitchSlider.setProgress(strategy.progressOf(pitch));
pitchSlider.setOnSeekBarChangeListener(getOnPitchChangedListener());
}
}
@@ -220,7 +226,7 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupHookingControl(@NonNull View rootView) {
unhookingCheckbox = rootView.findViewById(R.id.unhookCheckbox);
if (unhookingCheckbox != null) {
unhookingCheckbox.setChecked(initialPitch != initialTempo);
unhookingCheckbox.setChecked(pitch != tempo);
unhookingCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
if (isChecked) return;
// When unchecked, slide back to the minimum of current tempo or pitch
@@ -231,24 +237,84 @@ public class PlaybackParameterDialog extends DialogFragment {
}
}
private void setupPresetControl(@NonNull View rootView) {
nightCorePresetText = rootView.findViewById(R.id.presetNightcore);
if (nightCorePresetText != null) {
nightCorePresetText.setOnClickListener(view -> {
final double randomPitch = NIGHTCORE_PITCH_LOWER +
Math.random() * (NIGHTCORE_PITCH_UPPER - NIGHTCORE_PITCH_LOWER);
private void setupSkipSilenceControl(@NonNull View rootView) {
skipSilenceCheckbox = rootView.findViewById(R.id.skipSilenceCheckbox);
if (skipSilenceCheckbox != null) {
skipSilenceCheckbox.setChecked(initialSkipSilence);
skipSilenceCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) ->
setCurrentPlaybackParameters());
}
}
setTempoSlider(NIGHTCORE_TEMPO);
setPitchSlider(randomPitch);
private void setupStepSizeSelector(@NonNull final View rootView) {
stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
stepSizeTenPercentText = rootView.findViewById(R.id.stepSizeTenPercent);
stepSizeTwentyFivePercentText = rootView.findViewById(R.id.stepSizeTwentyFivePercent);
stepSizeOneHundredPercentText = rootView.findViewById(R.id.stepSizeOneHundredPercent);
if (stepSizeOnePercentText != null) {
stepSizeOnePercentText.setText(getPercentString(STEP_ONE_PERCENT_VALUE));
stepSizeOnePercentText.setOnClickListener(view ->
changeStepSize(STEP_ONE_PERCENT_VALUE));
}
if (stepSizeFivePercentText != null) {
stepSizeFivePercentText.setText(getPercentString(STEP_FIVE_PERCENT_VALUE));
stepSizeFivePercentText.setOnClickListener(view ->
changeStepSize(STEP_FIVE_PERCENT_VALUE));
}
if (stepSizeTenPercentText != null) {
stepSizeTenPercentText.setText(getPercentString(STEP_TEN_PERCENT_VALUE));
stepSizeTenPercentText.setOnClickListener(view ->
changeStepSize(STEP_TEN_PERCENT_VALUE));
}
if (stepSizeTwentyFivePercentText != null) {
stepSizeTwentyFivePercentText.setText(getPercentString(STEP_TWENTY_FIVE_PERCENT_VALUE));
stepSizeTwentyFivePercentText.setOnClickListener(view ->
changeStepSize(STEP_TWENTY_FIVE_PERCENT_VALUE));
}
if (stepSizeOneHundredPercentText != null) {
stepSizeOneHundredPercentText.setText(getPercentString(STEP_ONE_HUNDRED_PERCENT_VALUE));
stepSizeOneHundredPercentText.setOnClickListener(view ->
changeStepSize(STEP_ONE_HUNDRED_PERCENT_VALUE));
}
}
private void changeStepSize(final double stepSize) {
this.stepSize = stepSize;
if (tempoStepUpText != null) {
tempoStepUpText.setText(getStepUpPercentString(stepSize));
tempoStepUpText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() + stepSize);
setCurrentPlaybackParameters();
});
}
resetPresetText = rootView.findViewById(R.id.presetReset);
if (resetPresetText != null) {
resetPresetText.setOnClickListener(view -> {
setTempoSlider(DEFAULT_TEMPO);
setPitchSlider(DEFAULT_PITCH);
if (tempoStepDownText != null) {
tempoStepDownText.setText(getStepDownPercentString(stepSize));
tempoStepDownText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() - stepSize);
setCurrentPlaybackParameters();
});
}
if (pitchStepUpText != null) {
pitchStepUpText.setText(getStepUpPercentString(stepSize));
pitchStepUpText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() + stepSize);
setCurrentPlaybackParameters();
});
}
if (pitchStepDownText != null) {
pitchStepDownText.setText(getStepDownPercentString(stepSize));
pitchStepDownText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() - stepSize);
setCurrentPlaybackParameters();
});
}
@@ -342,10 +408,11 @@ public class PlaybackParameterDialog extends DialogFragment {
//////////////////////////////////////////////////////////////////////////*/
private void setCurrentPlaybackParameters() {
setPlaybackParameters(getCurrentTempo(), getCurrentPitch());
setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence());
}
private void setPlaybackParameters(final double tempo, final double pitch) {
private void setPlaybackParameters(final double tempo, final double pitch,
final boolean skipSilence) {
if (callback != null && tempoCurrentText != null && pitchCurrentText != null) {
if (DEBUG) Log.d(TAG, "Setting playback parameters to " +
"tempo=[" + tempo + "], " +
@@ -353,27 +420,40 @@ public class PlaybackParameterDialog extends DialogFragment {
tempoCurrentText.setText(PlayerHelper.formatSpeed(tempo));
pitchCurrentText.setText(PlayerHelper.formatPitch(pitch));
callback.onPlaybackParameterChanged((float) tempo, (float) pitch);
callback.onPlaybackParameterChanged((float) tempo, (float) pitch, skipSilence);
}
}
private double getCurrentTempo() {
return tempoSlider == null ? initialTempo : strategy.valueOf(
return tempoSlider == null ? tempo : strategy.valueOf(
tempoSlider.getProgress());
}
private double getCurrentPitch() {
return pitchSlider == null ? initialPitch : strategy.valueOf(
return pitchSlider == null ? pitch : strategy.valueOf(
pitchSlider.getProgress());
}
private double getCurrentStepSize() {
return stepSize;
}
private boolean getCurrentSkipSilence() {
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked();
}
@NonNull
private static String getStepUpPercentString(final double percent) {
return STEP_UP_SIGN + PlayerHelper.formatPitch(percent);
return STEP_UP_SIGN + getPercentString(percent);
}
@NonNull
private static String getStepDownPercentString(final double percent) {
return STEP_DOWN_SIGN + PlayerHelper.formatPitch(percent);
return STEP_DOWN_SIGN + getPercentString(percent);
}
@NonNull
private static String getPercentString(final double percent) {
return PlayerHelper.formatPitch(percent);
}
}

View File

@@ -4,14 +4,15 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.accessibility.CaptioningManager;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes;
@@ -28,6 +29,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import java.lang.annotation.Retention;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@@ -37,10 +39,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FILL;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.*;
public class PlayerHelper {
private PlayerHelper() {}
@@ -50,6 +55,14 @@ public class PlayerHelper {
private static final NumberFormat speedFormatter = new DecimalFormat("0.##x");
private static final NumberFormat pitchFormatter = new DecimalFormat("##%");
@Retention(SOURCE)
@IntDef({MINIMIZE_ON_EXIT_MODE_NONE, MINIMIZE_ON_EXIT_MODE_BACKGROUND,
MINIMIZE_ON_EXIT_MODE_POPUP})
public @interface MinimizeMode {
int MINIMIZE_ON_EXIT_MODE_NONE = 0;
int MINIMIZE_ON_EXIT_MODE_BACKGROUND = 1;
int MINIMIZE_ON_EXIT_MODE_POPUP = 2;
}
////////////////////////////////////////////////////////////////////////////
// Exposed helpers
////////////////////////////////////////////////////////////////////////////
@@ -172,6 +185,22 @@ public class PlayerHelper {
return isAutoQueueEnabled(context, false);
}
@MinimizeMode
public static int getMinimizeOnExitAction(@NonNull final Context context) {
final String defaultAction = context.getString(R.string.minimize_on_exit_none_key);
final String popupAction = context.getString(R.string.minimize_on_exit_popup_key);
final String backgroundAction = context.getString(R.string.minimize_on_exit_background_key);
final String action = getMinimizeOnExitAction(context, defaultAction);
if (action.equals(popupAction)) {
return MINIMIZE_ON_EXIT_MODE_POPUP;
} else if (action.equals(backgroundAction)) {
return MINIMIZE_ON_EXIT_MODE_BACKGROUND;
} else {
return MINIMIZE_ON_EXIT_MODE_NONE;
}
}
@NonNull
public static SeekParameters getSeekParameters(@NonNull final Context context) {
return isUsingInexactSeek(context, false) ?
@@ -212,7 +241,6 @@ public class PlayerHelper {
public static TrackSelection.Factory getQualitySelector(@NonNull final Context context,
@NonNull final BandwidthMeter meter) {
return new AdaptiveTrackSelection.Factory(meter,
AdaptiveTrackSelection.DEFAULT_MAX_INITIAL_BITRATE,
/*bufferDurationRequiredForQualityIncrease=*/1000,
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
@@ -224,7 +252,7 @@ public class PlayerHelper {
}
public static int getShutdownFlingVelocity(@NonNull final Context context) {
return 10000;
return 6000;
}
public static int getTossFlingVelocity(@NonNull final Context context) {
@@ -248,7 +276,6 @@ public class PlayerHelper {
* System font scaling:
* Very small - 0.25f, Small - 0.5f, Normal - 1.0f, Large - 1.5f, Very Large - 2.0f
* */
@NonNull
public static float getCaptionScale(@NonNull final Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return 1f;
@@ -260,6 +287,16 @@ public class PlayerHelper {
return captioningManager.getFontScale();
}
public static float getScreenBrightness(@NonNull final Context context) {
//a value of less than 0, the default, means to use the preferred screen brightness
return getScreenBrightness(context, -1);
}
public static void setScreenBrightness(@NonNull final Context context, final float setScreenBrightness) {
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
}
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
@@ -292,4 +329,29 @@ public class PlayerHelper {
private static boolean isAutoQueueEnabled(@NonNull final Context context, final boolean b) {
return getPreferences(context).getBoolean(context.getString(R.string.auto_queue_key), b);
}
private static void setScreenBrightness(@NonNull final Context context, final float screenBrightness, final long timestamp) {
SharedPreferences.Editor editor = getPreferences(context).edit();
editor.putFloat(context.getString(R.string.screen_brightness_key), screenBrightness);
editor.putLong(context.getString(R.string.screen_brightness_timestamp_key), timestamp);
editor.apply();
}
private static float getScreenBrightness(@NonNull final Context context, final float screenBrightness) {
SharedPreferences sp = getPreferences(context);
long timestamp = sp.getLong(context.getString(R.string.screen_brightness_timestamp_key), 0);
// hypothesis: 4h covers a viewing block, eg evening. External lightning conditions will change in the next
// viewing block so we fall back to the default brightness
if ((System.currentTimeMillis() - timestamp) > TimeUnit.HOURS.toMillis(4)) {
return screenBrightness;
} else {
return sp.getFloat(context.getString(R.string.screen_brightness_key), screenBrightness);
}
}
private static String getMinimizeOnExitAction(@NonNull final Context context,
final String key) {
return getPreferences(context).getString(context.getString(R.string.minimize_on_exit_key),
key);
}
}

View File

@@ -1,45 +0,0 @@
package org.schabi.newpipe.player.mediasession;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
public class DummyPlaybackPreparer implements MediaSessionConnector.PlaybackPreparer {
@Override
public long getSupportedPrepareActions() {
return 0;
}
@Override
public void onPrepare() {
}
@Override
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
}
@Override
public void onPrepareFromSearch(String query, Bundle extras) {
}
@Override
public void onPrepareFromUri(Uri uri, Bundle extras) {
}
@Override
public String[] getCommands() {
return new String[0];
}
@Override
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
}
}

View File

@@ -13,5 +13,4 @@ public interface MediaSessionCallback {
void onPlay();
void onPause();
void onSetShuffle(final boolean isShuffled);
}

View File

@@ -1,7 +1,5 @@
package org.schabi.newpipe.player.mediasession;
import android.support.v4.media.session.PlaybackStateCompat;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ext.mediasession.DefaultPlaybackController;
@@ -22,10 +20,4 @@ public class PlayQueuePlaybackController extends DefaultPlaybackController {
public void onPause(Player player) {
callback.onPause();
}
@Override
public void onSetShuffleMode(Player player, int shuffleMode) {
callback.onSetShuffle(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL
|| shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP);
}
}

View File

@@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.upstream.Allocator;
@@ -11,7 +12,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.io.IOException;
public class FailedMediaSource implements ManagedMediaSource {
public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSource {
private final String TAG = "FailedMediaSource@" + Integer.toHexString(hashCode());
public static class FailedMediaSourceException extends Exception {
@@ -72,11 +73,6 @@ public class FailedMediaSource implements ManagedMediaSource {
return System.currentTimeMillis() >= retryTimestamp;
}
@Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
Log.e(TAG, "Loading failed source: ", error);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
throw new IOException(error);
@@ -90,8 +86,14 @@ public class FailedMediaSource implements ManagedMediaSource {
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {}
@Override
public void releaseSource() {}
protected void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
Log.e(TAG, "Loading failed source: ", error);
}
@Override
protected void releaseSourceInternal() {}
@Override
public boolean shouldBeReplacedWith(@NonNull final PlayQueueItem newIdentity,

View File

@@ -1,10 +1,12 @@
package org.schabi.newpipe.player.mediasource;
import android.os.Handler;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.upstream.Allocator;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
@@ -34,7 +36,8 @@ public class LoadedMediaSource implements ManagedMediaSource {
}
@Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
public void prepareSource(ExoPlayer player, boolean isTopLevelSource,
SourceInfoRefreshListener listener) {
source.prepareSource(player, isTopLevelSource, listener);
}
@@ -54,8 +57,18 @@ public class LoadedMediaSource implements ManagedMediaSource {
}
@Override
public void releaseSource() {
source.releaseSource();
public void releaseSource(SourceInfoRefreshListener listener) {
source.releaseSource(listener);
}
@Override
public void addEventListener(Handler handler, MediaSourceEventListener eventListener) {
source.addEventListener(handler, eventListener);
}
@Override
public void removeEventListener(MediaSourceEventListener eventListener) {
source.removeEventListener(eventListener);
}
@Override

View File

@@ -3,14 +3,14 @@ package org.schabi.newpipe.player.mediasource;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
public class ManagedMediaSourcePlaylist {
@NonNull private final DynamicConcatenatingMediaSource internalSource;
@NonNull private final ConcatenatingMediaSource internalSource;
public ManagedMediaSourcePlaylist() {
internalSource = new DynamicConcatenatingMediaSource(/*isPlaylistAtomic=*/false,
internalSource = new ConcatenatingMediaSource(/*isPlaylistAtomic=*/false,
new ShuffleOrder.UnshuffledShuffleOrder(0));
}
@@ -32,12 +32,8 @@ public class ManagedMediaSourcePlaylist {
null : (ManagedMediaSource) internalSource.getMediaSource(index);
}
public void dispose() {
internalSource.releaseSource();
}
@NonNull
public DynamicConcatenatingMediaSource getParentMediaSource() {
public ConcatenatingMediaSource getParentMediaSource() {
return internalSource;
}
@@ -46,7 +42,7 @@ public class ManagedMediaSourcePlaylist {
//////////////////////////////////////////////////////////////////////////*/
/**
* Expands the {@link DynamicConcatenatingMediaSource} by appending it with a
* Expands the {@link ConcatenatingMediaSource} by appending it with a
* {@link PlaceholderMediaSource}.
*
* @see #append(ManagedMediaSource)
@@ -56,17 +52,17 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Appends a {@link ManagedMediaSource} to the end of {@link DynamicConcatenatingMediaSource}.
* @see DynamicConcatenatingMediaSource#addMediaSource
* Appends a {@link ManagedMediaSource} to the end of {@link ConcatenatingMediaSource}.
* @see ConcatenatingMediaSource#addMediaSource
* */
public synchronized void append(@NonNull final ManagedMediaSource source) {
internalSource.addMediaSource(source);
}
/**
* Removes a {@link ManagedMediaSource} from {@link DynamicConcatenatingMediaSource}
* Removes a {@link ManagedMediaSource} from {@link ConcatenatingMediaSource}
* at the given index. If this index is out of bound, then the removal is ignored.
* @see DynamicConcatenatingMediaSource#removeMediaSource(int)
* @see ConcatenatingMediaSource#removeMediaSource(int)
* */
public synchronized void remove(final int index) {
if (index < 0 || index > internalSource.getSize()) return;
@@ -75,10 +71,10 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Moves a {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Moves a {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* from the given source index to the target index. If either index is out of bound,
* then the call is ignored.
* @see DynamicConcatenatingMediaSource#moveMediaSource(int, int)
* @see ConcatenatingMediaSource#moveMediaSource(int, int)
* */
public synchronized void move(final int source, final int target) {
if (source < 0 || target < 0) return;
@@ -99,7 +95,7 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Updates the {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* at the given index with a given {@link ManagedMediaSource}.
* @see #update(int, ManagedMediaSource, Runnable)
* */
@@ -108,11 +104,11 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Updates the {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* at the given index with a given {@link ManagedMediaSource}. If the index is out of bound,
* then the replacement is ignored.
* @see DynamicConcatenatingMediaSource#addMediaSource
* @see DynamicConcatenatingMediaSource#removeMediaSource(int, Runnable)
* @see ConcatenatingMediaSource#addMediaSource
* @see ConcatenatingMediaSource#removeMediaSource(int, Runnable)
* */
public synchronized void update(final int index, @NonNull final ManagedMediaSource source,
@Nullable final Runnable finalizingAction) {

View File

@@ -3,20 +3,19 @@ package org.schabi.newpipe.player.mediasource;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.upstream.Allocator;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.io.IOException;
public class PlaceholderMediaSource implements ManagedMediaSource {
public class PlaceholderMediaSource extends BaseMediaSource implements ManagedMediaSource {
// Do nothing, so this will stall the playback
@Override public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {}
@Override public void maybeThrowSourceInfoRefreshError() throws IOException {}
@Override public void maybeThrowSourceInfoRefreshError() {}
@Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { return null; }
@Override public void releasePeriod(MediaPeriod mediaPeriod) {}
@Override public void releaseSource() {}
@Override protected void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {}
@Override protected void releaseSourceInternal() {}
@Override
public boolean shouldBeReplacedWith(@NonNull PlayQueueItem newIdentity,

View File

@@ -69,9 +69,4 @@ public class BasePlayerMediaSession implements MediaSessionCallback {
public void onPause() {
player.onPause();
}
@Override
public void onSetShuffle(boolean isShuffled) {
player.onShuffleModeEnabledChanged(isShuffled);
}
}

View File

@@ -5,12 +5,10 @@ import android.support.annotation.Nullable;
import android.support.v4.util.ArraySet;
import android.util.Log;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.mediasource.FailedMediaSource;
import org.schabi.newpipe.player.mediasource.LoadedMediaSource;
import org.schabi.newpipe.player.mediasource.ManagedMediaSource;
@@ -24,10 +22,8 @@ import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
import org.schabi.newpipe.player.playqueue.events.ReorderEvent;
import org.schabi.newpipe.util.ServiceHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -37,8 +33,6 @@ import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.SerialDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.internal.subscriptions.EmptySubscription;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
@@ -104,7 +98,6 @@ public class MediaSourceManager {
private final static int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
@NonNull private final CompositeDisposable loaderReactor;
@NonNull private final Set<PlayQueueItem> loadingItems;
@NonNull private final SerialDisposable syncReactor;
@NonNull private final AtomicBoolean isBlocked;
@@ -144,7 +137,6 @@ public class MediaSourceManager {
this.playQueueReactor = EmptySubscription.INSTANCE;
this.loaderReactor = new CompositeDisposable();
this.syncReactor = new SerialDisposable();
this.isBlocked = new AtomicBoolean(false);
@@ -171,8 +163,6 @@ public class MediaSourceManager {
playQueueReactor.cancel();
loaderReactor.dispose();
syncReactor.dispose();
playlist.dispose();
}
/*//////////////////////////////////////////////////////////////////////////
@@ -311,21 +301,7 @@ public class MediaSourceManager {
final PlayQueueItem currentItem = playQueue.getItem();
if (isBlocked.get() || currentItem == null) return;
final Consumer<StreamInfo> onSuccess = info -> syncInternal(currentItem, info);
final Consumer<Throwable> onError = throwable -> syncInternal(currentItem, null);
final Disposable sync = currentItem.getStream()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onSuccess, onError);
syncReactor.set(sync);
}
private void syncInternal(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info) {
// Ensure the current item is up to date with the play queue
if (playQueue.getItem() == item) {
playbackListener.onPlaybackSynchronize(item, info);
}
playbackListener.onPlaybackSynchronize(currentItem);
}
private synchronized void maybeSynchronizePlayer() {
@@ -424,7 +400,8 @@ public class MediaSourceManager {
}
/**
* Checks if the corresponding MediaSource in {@link DynamicConcatenatingMediaSource}
* Checks if the corresponding MediaSource in
* {@link com.google.android.exoplayer2.source.ConcatenatingMediaSource}
* for a given {@link PlayQueueItem} needs replacement, either due to gapless playback
* readiness or playlist desynchronization.
* <br><br>
@@ -481,8 +458,6 @@ public class MediaSourceManager {
private void resetSources() {
if (DEBUG) Log.d(TAG, "resetSources() called.");
playlist.dispose();
playlist = new ManagedMediaSourcePlaylist();
}

View File

@@ -45,7 +45,7 @@ public interface PlaybackListener {
*
* May be called anytime at any amount once unblock is called.
* */
void onPlaybackSynchronize(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info);
void onPlaybackSynchronize(@NonNull final PlayQueueItem item);
/**
* Requests the listener to resolve a stream info into a media source

View File

@@ -14,6 +14,7 @@ import org.schabi.newpipe.player.playqueue.events.MoveEvent;
import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent;
import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
import org.schabi.newpipe.player.playqueue.events.SelectEvent;
import org.schabi.newpipe.util.FallbackViewHolder;
import java.util.List;
@@ -188,7 +189,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
return new PlayQueueItemHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.play_queue_item, parent, false));
default:
Log.e(TAG, "Attempting to create view holder with undefined type: " + type);
return null;
return new FallbackViewHolder(new View(parent.getContext()));
}
}

View File

@@ -0,0 +1,41 @@
package org.schabi.newpipe.player.resolver;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;
public class AudioPlaybackResolver implements PlaybackResolver {
@NonNull private final Context context;
@NonNull private final PlayerDataSource dataSource;
public AudioPlaybackResolver(@NonNull final Context context,
@NonNull final PlayerDataSource dataSource) {
this.context = context;
this.dataSource = dataSource;
}
@Override
@Nullable
public MediaSource resolve(@NonNull StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
if (liveSource != null) return liveSource;
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index < 0 || index >= info.getAudioStreams().size()) return null;
final AudioStream audio = info.getAudioStreams().get(index);
final MediaSourceTag tag = new MediaSourceTag(info);
return buildMediaSource(dataSource, audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()), tag);
}
}

View File

@@ -0,0 +1,51 @@
package org.schabi.newpipe.player.resolver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
public class MediaSourceTag implements Serializable {
@NonNull private final StreamInfo metadata;
@NonNull private final List<VideoStream> sortedAvailableVideoStreams;
private final int selectedVideoStreamIndex;
public MediaSourceTag(@NonNull final StreamInfo metadata,
@NonNull final List<VideoStream> sortedAvailableVideoStreams,
final int selectedVideoStreamIndex) {
this.metadata = metadata;
this.sortedAvailableVideoStreams = sortedAvailableVideoStreams;
this.selectedVideoStreamIndex = selectedVideoStreamIndex;
}
public MediaSourceTag(@NonNull final StreamInfo metadata) {
this(metadata, Collections.emptyList(), /*indexNotAvailable=*/-1);
}
@NonNull
public StreamInfo getMetadata() {
return metadata;
}
@NonNull
public List<VideoStream> getSortedAvailableVideoStreams() {
return sortedAvailableVideoStreams;
}
public int getSelectedVideoStreamIndex() {
return selectedVideoStreamIndex;
}
@Nullable
public VideoStream getSelectedVideoStream() {
return selectedVideoStreamIndex < 0 ||
selectedVideoStreamIndex >= sortedAvailableVideoStreams.size() ? null :
sortedAvailableVideoStreams.get(selectedVideoStreamIndex);
}
}

View File

@@ -0,0 +1,84 @@
package org.schabi.newpipe.player.resolver;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.util.Util;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.player.helper.PlayerDataSource;
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
@Nullable
default MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final StreamInfo info) {
final StreamType streamType = info.getStreamType();
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
return null;
}
final MediaSourceTag tag = new MediaSourceTag(info);
if (!info.getHlsUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.TYPE_HLS, tag);
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getDashMpdUrl(), C.TYPE_DASH, tag);
}
return null;
}
@NonNull
default MediaSource buildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String sourceUrl,
@C.ContentType final int type,
@NonNull final MediaSourceTag metadata) {
final Uri uri = Uri.parse(sourceUrl);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getLiveDashMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getLiveHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
@NonNull
default MediaSource buildMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String sourceUrl,
@NonNull final String cacheKey,
@NonNull final String overrideExtension,
@NonNull final MediaSourceTag metadata) {
final Uri uri = Uri.parse(sourceUrl);
@C.ContentType final int type = TextUtils.isEmpty(overrideExtension) ?
Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getDashMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_OTHER:
return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata)
.createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
}

View File

@@ -0,0 +1,8 @@
package org.schabi.newpipe.player.resolver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public interface Resolver<Source, Product> {
@Nullable Product resolve(@NonNull Source source);
}

View File

@@ -0,0 +1,123 @@
package org.schabi.newpipe.player.resolver;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.Subtitles;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.exoplayer2.C.SELECTION_FLAG_AUTOSELECT;
import static com.google.android.exoplayer2.C.TIME_UNSET;
public class VideoPlaybackResolver implements PlaybackResolver {
public interface QualityResolver {
int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality);
}
@NonNull private final Context context;
@NonNull private final PlayerDataSource dataSource;
@NonNull private final QualityResolver qualityResolver;
@Nullable private String playbackQuality;
public VideoPlaybackResolver(@NonNull final Context context,
@NonNull final PlayerDataSource dataSource,
@NonNull final QualityResolver qualityResolver) {
this.context = context;
this.dataSource = dataSource;
this.qualityResolver = qualityResolver;
}
@Override
@Nullable
public MediaSource resolve(@NonNull StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
if (liveSource != null) return liveSource;
List<MediaSource> mediaSources = new ArrayList<>();
// Create video stream source
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
final int index;
if (videos.isEmpty()) {
index = -1;
} else if (playbackQuality == null) {
index = qualityResolver.getDefaultResolutionIndex(videos);
} else {
index = qualityResolver.getOverrideResolutionIndex(videos, getPlaybackQuality());
}
final MediaSourceTag tag = new MediaSourceTag(info, videos, index);
@Nullable final VideoStream video = tag.getSelectedVideoStream();
if (video != null) {
final MediaSource streamSource = buildMediaSource(dataSource, video.getUrl(),
PlayerHelper.cacheKeyOf(info, video),
MediaFormat.getSuffixById(video.getFormatId()), tag);
mediaSources.add(streamSource);
}
// Create optional audio stream source
final List<AudioStream> audioStreams = info.getAudioStreams();
final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
ListHelper.getDefaultAudioFormat(context, audioStreams));
// Use the audio stream if there is no video stream, or
// Merge with audio stream in case if video does not contain audio
if (audio != null && ((video != null && video.isVideoOnly) || video == null)) {
final MediaSource audioSource = buildMediaSource(dataSource, audio.getUrl(),
PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()), tag);
mediaSources.add(audioSource);
}
// If there is no audio or video sources, then this media source cannot be played back
if (mediaSources.isEmpty()) return null;
// Below are auxiliary media sources
// Create subtitle sources
for (final Subtitles subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFileType());
if (mimeType == null) continue;
final Format textFormat = Format.createTextSampleFormat(null, mimeType,
SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle));
final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
.createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
mediaSources.add(textSource);
}
if (mediaSources.size() == 1) {
return mediaSources.get(0);
} else {
return new MergingMediaSource(mediaSources.toArray(
new MediaSource[mediaSources.size()]));
}
}
@Nullable
public String getPlaybackQuality() {
return playbackQuality;
}
public void setPlaybackQuality(@Nullable String playbackQuality) {
this.playbackQuality = playbackQuality;
}
}

View File

@@ -1,7 +1,9 @@
package org.schabi.newpipe.report;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
@@ -33,10 +35,8 @@ import org.json.JSONArray;
import org.json.JSONObject;
import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.PrintWriter;
@@ -44,9 +44,9 @@ import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
/*
* Created by Christian Schabesberger on 24.10.15.
@@ -210,12 +210,31 @@ public class ErrorActivity extends AppCompatActivity {
currentTimeStamp = getCurrentTimeStamp();
reportButton.setOnClickListener((View v) -> {
Intent i = new Intent(Intent.ACTION_SENDTO);
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
.putExtra(Intent.EXTRA_TEXT, buildJson());
Context context = this;
new AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.privacy_policy_title)
.setMessage(R.string.start_accept_privacy_policy)
.setCancelable(false)
.setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> {
Intent webIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse(context.getString(R.string.privacy_policy_url))
);
context.startActivity(webIntent);
})
.setPositiveButton(R.string.accept, (dialog, which) -> {
Intent i = new Intent(Intent.ACTION_SENDTO);
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
.putExtra(Intent.EXTRA_TEXT, buildJson());
startActivity(Intent.createChooser(i, "Send Email"));
})
.setNegativeButton(R.string.decline, (dialog, which) -> {
// do nothing
})
.show();
startActivity(Intent.createChooser(i, "Send Email"));
});
// normal bugreport

View File

@@ -4,7 +4,9 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.preference.ListPreference;
@@ -30,15 +32,21 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static android.content.Context.MODE_PRIVATE;
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945;
@@ -48,6 +56,9 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
private File databasesDir;
private File newpipe_db;
private File newpipe_db_journal;
private File newpipe_db_shm;
private File newpipe_db_wal;
private File newpipe_settings;
private String thumbnailLoadToggleKey;
@@ -79,6 +90,11 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
databasesDir = new File(homeDir + "/databases");
newpipe_db = new File(homeDir + "/databases/newpipe.db");
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
newpipe_db_shm = new File(homeDir + "/databases/newpipe.db-shm");
newpipe_db_wal = new File(homeDir + "/databases/newpipe.db-wal");
newpipe_settings = new File(homeDir + "/databases/newpipe.settings");
newpipe_settings.delete();
addPreferencesFromResource(R.xml.content_settings);
@@ -174,19 +190,19 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
&& resultCode == Activity.RESULT_OK && data.getData() != null) {
String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
if (requestCode == REQUEST_EXPORT_PATH) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(android.R.string.ok,
(DialogInterface d, int id) -> importDatabase(path))
.setNegativeButton(android.R.string.cancel,
(DialogInterface d, int id) -> d.cancel());
builder.create().show();
}
String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
if (requestCode == REQUEST_EXPORT_PATH) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(android.R.string.ok,
(DialogInterface d, int id) -> importDatabase(path))
.setNegativeButton(android.R.string.cancel,
(DialogInterface d, int id) -> d.cancel());
builder.create().show();
}
}
}
@@ -196,7 +212,9 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
new BufferedOutputStream(
new FileOutputStream(path)));
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
ZipHelper.addFileToZip(outZip, newpipe_db_journal.getPath(), "newpipe.db-journal");
saveSharedPreferencesToFile(newpipe_settings);
ZipHelper.addFileToZip(outZip, newpipe_settings.getPath(), "newpipe.settings");
outZip.close();
@@ -207,6 +225,29 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
private void saveSharedPreferencesToFile(File dst) {
ObjectOutputStream output = null;
try {
output = new ObjectOutputStream(new FileOutputStream(dst));
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
output.writeObject(pref.getAll());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (output != null) {
output.flush();
output.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private void importDatabase(String filePath) {
// check if file is supported
ZipFile zipFile = null;
@@ -223,30 +264,91 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
try {
ZipInputStream zipIn = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(filePath)));
if (!databasesDir.exists() && !databasesDir.mkdir()) {
throw new Exception("Could not create databases dir");
}
if(!(ZipHelper.extractFileFromZip(zipIn, newpipe_db.getPath(), "newpipe.db")
&& ZipHelper.extractFileFromZip(zipIn, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
final boolean isDbFileExtracted = ZipHelper.extractFileFromZip(filePath,
newpipe_db.getPath(), "newpipe.db");
if (isDbFileExtracted) {
newpipe_db_journal.delete();
newpipe_db_wal.delete();
newpipe_db_shm.delete();
} else {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
}
zipIn.close();
//If settings file exist, ask if it should be imported.
if(ZipHelper.extractFileFromZip(filePath, newpipe_settings.getPath(), "newpipe.settings")) {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle(R.string.import_settings);
alert.setNegativeButton(android.R.string.no, (dialog, which) -> {
dialog.dismiss();
// restart app to properly load db
System.exit(0);
});
alert.setPositiveButton(android.R.string.yes, (dialog, which) -> {
dialog.dismiss();
loadSharedPreferences(newpipe_settings);
// restart app to properly load db
System.exit(0);
});
alert.show();
} else {
// restart app to properly load db
System.exit(0);
}
// restart app to properly load db
//App.restart(getContext());
System.exit(0);
} catch (Exception e) {
onError(e);
}
}
private void loadSharedPreferences(File src) {
ObjectInputStream input = null;
try {
input = new ObjectInputStream(new FileInputStream(src));
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
prefEdit.clear();
Map<String, ?> entries = (Map<String, ?>) input.readObject();
for (Map.Entry<String, ?> entry : entries.entrySet()) {
Object v = entry.getValue();
String key = entry.getKey();
if (v instanceof Boolean)
prefEdit.putBoolean(key, ((Boolean) v).booleanValue());
else if (v instanceof Float)
prefEdit.putFloat(key, ((Float) v).floatValue());
else if (v instanceof Integer)
prefEdit.putInt(key, ((Integer) v).intValue());
else if (v instanceof Long)
prefEdit.putLong(key, ((Long) v).longValue());
else if (v instanceof String)
prefEdit.putString(key, ((String) v));
}
prefEdit.commit();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if (input != null) {
input.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
@Override
public void onResume() {
super.onResume();

View File

@@ -159,7 +159,7 @@ public class SelectChannelFragment extends DialogFragment {
@Override
public void onError(Throwable exception) {
onError(exception);
SelectChannelFragment.this.onError(exception);
}
@Override

View File

@@ -6,7 +6,7 @@ public class Constants {
public static final String KEY_TITLE = "key_title";
public static final String KEY_LINK_TYPE = "key_link_type";
public static final String KEY_OPEN_SEARCH = "key_open_search";
public static final String KEY_QUERY = "key_query";
public static final String KEY_SEARCH_STRING = "key_search_string";
public static final String KEY_THEME_CHANGE = "key_theme_change";
public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change";

View File

@@ -37,9 +37,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@@ -50,7 +49,6 @@ import java.util.List;
import io.reactivex.Maybe;
import io.reactivex.Single;
import io.reactivex.annotations.NonNull;
public final class ExtractorHelper {
private static final String TAG = ExtractorHelper.class.getSimpleName();
@@ -66,29 +64,35 @@ public final class ExtractorHelper {
}
}
public static Single<SearchResult> searchFor(final int serviceId,
final String query,
final int pageNumber,
final String contentCountry,
final SearchEngine.Filter filter) {
public static Single<SearchInfo> searchFor(final int serviceId,
final String searchString,
final List<String> contentFilter,
final String sortFilter,
final String contentCountry) {
checkServiceId(serviceId);
return Single.fromCallable(() ->
SearchResult.getSearchResult(NewPipe.getService(serviceId).getSearchEngine(),
query, pageNumber, contentCountry, filter)
);
SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry));
}
public static Single<InfoItemsPage> getMoreSearchItems(final int serviceId,
final String query,
final int nextPageNumber,
final String searchLanguage,
final SearchEngine.Filter filter) {
final String searchString,
final List<String> contentFilter,
final String sortFilter,
final String pageUrl,
final String contentCountry) {
checkServiceId(serviceId);
return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter)
.map((@NonNull SearchResult searchResult) ->
new InfoItemsPage(searchResult.resultList,
nextPageNumber + "",
searchResult.errors));
return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl));
}
public static Single<List<String>> suggestionsFor(final int serviceId,
@@ -233,7 +237,6 @@ public final class ExtractorHelper {
serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId));
}
});
}
/**

View File

@@ -0,0 +1,10 @@
package org.schabi.newpipe.util;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class FallbackViewHolder extends RecyclerView.ViewHolder {
public FallbackViewHolder(View itemView) {
super(itemView);
}
}

View File

@@ -2,6 +2,7 @@ package org.schabi.newpipe.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.preference.PreferenceManager;
import android.support.annotation.StringRes;
@@ -13,56 +14,38 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@SuppressWarnings("WeakerAccess")
public final class ListHelper {
// Video format in order of quality. 0=lowest quality, n=highest quality
private static final List<MediaFormat> VIDEO_FORMAT_QUALITY_RANKING =
Arrays.asList(MediaFormat.v3GPP, MediaFormat.WEBM, MediaFormat.MPEG_4);
// Audio format in order of quality. 0=lowest quality, n=highest quality
private static final List<MediaFormat> AUDIO_FORMAT_QUALITY_RANKING =
Arrays.asList(MediaFormat.MP3, MediaFormat.WEBMA, MediaFormat.M4A);
// Audio format in order of efficiency. 0=most efficient, n=least efficient
private static final List<MediaFormat> AUDIO_FORMAT_EFFICIENCY_RANKING =
Arrays.asList(MediaFormat.WEBMA, MediaFormat.M4A, MediaFormat.MP3);
private static final List<String> HIGH_RESOLUTION_LIST = Arrays.asList("1440p", "2160p", "1440p60", "2160p60");
/**
* Return the index of the default stream in the list, based on the parameters
* defaultResolution and defaultFormat
*
* @return index of the default resolution&format
*/
public static int getDefaultResolutionIndex(String defaultResolution, String bestResolutionKey, MediaFormat defaultFormat, List<VideoStream> videoStreams) {
if (videoStreams == null || videoStreams.isEmpty()) return -1;
sortStreamList(videoStreams, false);
if (defaultResolution.equals(bestResolutionKey)) {
return 0;
}
int defaultStreamIndex = getDefaultStreamIndex(defaultResolution, defaultFormat, videoStreams);
if (defaultStreamIndex == -1 && defaultResolution.contains("p60")) {
defaultStreamIndex = getDefaultStreamIndex(defaultResolution.replace("p60", "p"), defaultFormat, videoStreams);
}
// this is actually an error,
// but maybe there is really no stream fitting to the default value.
if (defaultStreamIndex == -1) return 0;
return defaultStreamIndex;
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getDefaultResolutionIndex(Context context, List<VideoStream> videoStreams) {
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
return getDefaultResolutionIndex(context, videoStreams, defaultResolution);
String defaultResolution = computeDefaultResolution(context,
R.string.default_resolution_key, R.string.default_resolution_value);
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
public static int getResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
@@ -70,69 +53,29 @@ public final class ListHelper {
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getPopupDefaultResolutionIndex(Context context, List<VideoStream> videoStreams) {
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value));
return getPopupDefaultResolutionIndex(context, videoStreams, defaultResolution);
String defaultResolution = computeDefaultResolution(context,
R.string.default_popup_resolution_key, R.string.default_popup_resolution_value);
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getPopupDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
public static int getPopupResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
public static int getDefaultAudioFormat(Context context, List<AudioStream> audioStreams) {
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_audio_format_key, R.string.default_audio_format_value);
return getHighestQualityAudioIndex(defaultFormat, audioStreams);
}
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_audio_format_key,
R.string.default_audio_format_value);
public static int getHighestQualityAudioIndex(List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty()) return -1;
int highestQualityIndex = 0;
if (audioStreams.size() > 1) for (int i = 1; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
if (audioStream.getAverageBitrate() >= audioStreams.get(highestQualityIndex).getAverageBitrate()) highestQualityIndex = i;
// If the user has chosen to limit resolution to conserve mobile data
// usage then we should also limit our audio usage.
if (isLimitingDataUsage(context)) {
return getMostCompactAudioIndex(defaultFormat, audioStreams);
} else {
return getHighestQualityAudioIndex(defaultFormat, audioStreams);
}
return highestQualityIndex;
}
/**
* Get the audio from the list with the highest bitrate
*
* @param audioStreams list the audio streams
* @return audio with highest average bitrate
*/
public static AudioStream getHighestQualityAudio(List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty()) return null;
return audioStreams.get(getHighestQualityAudioIndex(audioStreams));
}
/**
* Get the audio from the list with the highest bitrate
*
* @param audioStreams list the audio streams
* @return index of the audio with the highest average bitrate of the default format
*/
public static int getHighestQualityAudioIndex(MediaFormat defaultFormat, List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty() || defaultFormat == null) return -1;
int highestQualityIndex = -1;
for (int i = 0; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
if (highestQualityIndex == -1 && audioStream.getFormat() == defaultFormat) highestQualityIndex = i;
if (highestQualityIndex != -1 && audioStream.getFormat() == defaultFormat
&& audioStream.getAverageBitrate() > audioStreams.get(highestQualityIndex).getAverageBitrate()) {
highestQualityIndex = i;
}
}
if (highestQualityIndex == -1) highestQualityIndex = getHighestQualityAudioIndex(audioStreams);
return highestQualityIndex;
}
/**
@@ -154,6 +97,50 @@ public final class ListHelper {
return getSortedStreamVideosList(defaultFormat, showHigherResolutions, videoStreams, videoOnlyStreams, ascendingOrder);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private static String computeDefaultResolution(Context context, int key, int value) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
// Load the prefered resolution otherwise the best available
String resolution = preferences != null
? preferences.getString(context.getString(key), context.getString(value))
: context.getString(R.string.best_resolution_key);
String maxResolution = getResolutionLimit(context);
if (maxResolution != null && compareVideoStreamResolution(maxResolution, resolution) < 1){
resolution = maxResolution;
}
return resolution;
}
/**
* Return the index of the default stream in the list, based on the parameters
* defaultResolution and defaultFormat
*
* @return index of the default resolution&format
*/
static int getDefaultResolutionIndex(String defaultResolution, String bestResolutionKey,
MediaFormat defaultFormat, List<VideoStream> videoStreams) {
if (videoStreams == null || videoStreams.isEmpty()) return -1;
sortStreamList(videoStreams, false);
if (defaultResolution.equals(bestResolutionKey)) {
return 0;
}
int defaultStreamIndex = getVideoStreamIndex(defaultResolution, defaultFormat, videoStreams);
// this is actually an error,
// but maybe there is really no stream fitting to the default value.
if (defaultStreamIndex == -1) {
return 0;
}
return defaultStreamIndex;
}
/**
* Join the two lists of video streams (video_only and normal videos), and sort them according with default format
* chosen by the user
@@ -165,7 +152,7 @@ public final class ListHelper {
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest @return the sorted list
* @return the sorted list
*/
public static List<VideoStream> getSortedStreamVideosList(MediaFormat defaultFormat, boolean showHigherResolutions, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
static List<VideoStream> getSortedStreamVideosList(MediaFormat defaultFormat, boolean showHigherResolutions, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
ArrayList<VideoStream> retList = new ArrayList<>();
HashMap<String, VideoStream> hashMap = new HashMap<>();
@@ -215,36 +202,138 @@ public final class ListHelper {
* @param videoStreams list that the sorting will be applied
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
*/
public static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
Collections.sort(videoStreams, new Comparator<VideoStream>() {
@Override
public int compare(VideoStream o1, VideoStream o2) {
int res1 = Integer.parseInt(o1.getResolution().replace("0p60", "1").replaceAll("[^\\d.]", ""));
int res2 = Integer.parseInt(o2.getResolution().replace("0p60", "1").replaceAll("[^\\d.]", ""));
return ascendingOrder ? res1 - res2 : res2 - res1;
}
private static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
Collections.sort(videoStreams, (o1, o2) -> {
int result = compareVideoStreamResolution(o1, o2, VIDEO_FORMAT_QUALITY_RANKING);
return result == 0 ? 0 : (ascendingOrder ? result : -result);
});
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
/**
* Get the audio from the list with the highest quality. Format will be ignored if it yields
* no results.
*
* @param audioStreams list the audio streams
* @return index of the audio with the highest average bitrate of the default format
*/
static int getHighestQualityAudioIndex(MediaFormat format, List<AudioStream> audioStreams) {
int result = -1;
if (audioStreams != null) {
while(result == -1) {
AudioStream prevStream = null;
for (int idx = 0; idx < audioStreams.size(); idx++) {
AudioStream stream = audioStreams.get(idx);
if ((format == null || stream.getFormat() == format) &&
(prevStream == null || compareAudioStreamBitrate(prevStream, stream,
AUDIO_FORMAT_QUALITY_RANKING) < 0)) {
prevStream = stream;
result = idx;
}
}
if (result == -1 && format == null) {
break;
}
format = null;
}
}
return result;
}
private static int getDefaultStreamIndex(String defaultResolution, MediaFormat defaultFormat, List<VideoStream> videoStreams) {
int defaultStreamIndex = -1;
for (int i = 0; i < videoStreams.size(); i++) {
VideoStream stream = videoStreams.get(i);
if (defaultStreamIndex == -1 && stream.getResolution().equals(defaultResolution)) defaultStreamIndex = i;
/**
* Get the audio from the list with the lowest bitrate and efficient format. Format will be
* ignored if it yields no results.
*
* @param format The target format type or null if it doesn't matter
* @param audioStreams list the audio streams
* @return index of the audio stream that can produce the most compact results or -1 if not found.
*/
static int getMostCompactAudioIndex(MediaFormat format, List<AudioStream> audioStreams) {
int result = -1;
if (audioStreams != null) {
while(result == -1) {
AudioStream prevStream = null;
for (int idx = 0; idx < audioStreams.size(); idx++) {
AudioStream stream = audioStreams.get(idx);
if ((format == null || stream.getFormat() == format) &&
(prevStream == null || compareAudioStreamBitrate(prevStream, stream,
AUDIO_FORMAT_EFFICIENCY_RANKING) > 0)) {
prevStream = stream;
result = idx;
}
}
if (result == -1 && format == null) {
break;
}
format = null;
}
}
return result;
}
if (stream.getFormat() == defaultFormat && stream.getResolution().equals(defaultResolution)) {
return i;
/**
* Locates a possible match for the given resolution and format in the provided list.
* In this order:
* 1. Find a format and resolution match
* 2. Find a format and resolution match and ignore the refresh
* 3. Find a resolution match
* 4. Find a resolution match and ignore the refresh
* 5. Find a resolution just below the requested resolution and ignore the refresh
* 6. Give up
*/
static int getVideoStreamIndex(String targetResolution, MediaFormat targetFormat,
List<VideoStream> videoStreams) {
int fullMatchIndex = -1;
int fullMatchNoRefreshIndex = -1;
int resMatchOnlyIndex = -1;
int resMatchOnlyNoRefreshIndex = -1;
int lowerResMatchNoRefreshIndex = -1;
String targetResolutionNoRefresh = targetResolution.replaceAll("p\\d+$", "p");
for (int idx = 0; idx < videoStreams.size(); idx++) {
MediaFormat format = targetFormat == null ? null : videoStreams.get(idx).getFormat();
String resolution = videoStreams.get(idx).getResolution();
String resolutionNoRefresh = resolution.replaceAll("p\\d+$", "p");
if (format == targetFormat && resolution.equals(targetResolution)) {
fullMatchIndex = idx;
}
if (format == targetFormat && resolutionNoRefresh.equals(targetResolutionNoRefresh)) {
fullMatchNoRefreshIndex = idx;
}
if (resMatchOnlyIndex == -1 && resolution.equals(targetResolution)) {
resMatchOnlyIndex = idx;
}
if (resMatchOnlyNoRefreshIndex == -1 && resolutionNoRefresh.equals(targetResolutionNoRefresh)) {
resMatchOnlyNoRefreshIndex = idx;
}
if (lowerResMatchNoRefreshIndex == -1 && compareVideoStreamResolution(resolutionNoRefresh, targetResolutionNoRefresh) < 0) {
lowerResMatchNoRefreshIndex = idx;
}
}
return defaultStreamIndex;
if (fullMatchIndex != -1) {
return fullMatchIndex;
}
if (fullMatchNoRefreshIndex != -1) {
return fullMatchNoRefreshIndex;
}
if (resMatchOnlyIndex != -1) {
return resMatchOnlyIndex;
}
if (resMatchOnlyNoRefreshIndex != -1) {
return resMatchOnlyNoRefreshIndex;
}
return lowerResMatchNoRefreshIndex;
}
/**
* Fetches the desired resolution or returns the default if it is not found. The resolution
* will be reduced if video chocking is active.
*/
private static int getDefaultResolutionWithDefaultFormat(Context context, String defaultResolution, List<VideoStream> videoStreams) {
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_video_format_key, R.string.default_video_format_value);
return getDefaultResolutionIndex(defaultResolution, context.getString(R.string.best_resolution_key), defaultFormat, videoStreams);
@@ -280,4 +369,85 @@ public final class ListHelper {
}
return format;
}
// Compares the quality of two audio streams
private static int compareAudioStreamBitrate(AudioStream streamA, AudioStream streamB,
List<MediaFormat> formatRanking) {
if (streamA == null) {
return -1;
}
if (streamB == null) {
return 1;
}
if (streamA.getAverageBitrate() < streamB.getAverageBitrate()) {
return -1;
}
if (streamA.getAverageBitrate() > streamB.getAverageBitrate()) {
return 1;
}
// Same bitrate and format
return formatRanking.indexOf(streamA.getFormat()) - formatRanking.indexOf(streamB.getFormat());
}
private static int compareVideoStreamResolution(String r1, String r2) {
int res1 = Integer.parseInt(r1.replaceAll("0p\\d+$", "1")
.replaceAll("[^\\d.]", ""));
int res2 = Integer.parseInt(r2.replaceAll("0p\\d+$", "1")
.replaceAll("[^\\d.]", ""));
return res1 - res2;
}
// Compares the quality of two video streams.
private static int compareVideoStreamResolution(VideoStream streamA, VideoStream streamB,
List<MediaFormat> formatRanking) {
if (streamA == null) {
return -1;
}
if (streamB == null) {
return 1;
}
int resComp = compareVideoStreamResolution(streamA.getResolution(), streamB.getResolution());
if (resComp != 0) {
return resComp;
}
// Same bitrate and format
return formatRanking.indexOf(streamA.getFormat()) - formatRanking.indexOf(streamB.getFormat());
}
private static boolean isLimitingDataUsage(Context context) {
return getResolutionLimit(context) != null;
}
/**
* The maximum resolution allowed
* @param context App context
* @return maximum resolution allowed or null if there is no maximum
*/
private static String getResolutionLimit(Context context) {
String resolutionLimit = null;
if (!isWifiActive(context)) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String defValue = context.getString(R.string.limit_data_usage_none_key);
String value = preferences.getString(
context.getString(R.string.limit_mobile_data_usage_key), defValue);
resolutionLimit = value.equals(defValue) ? null : value;
}
return resolutionLimit;
}
/**
* Are we connected to wifi?
* @param context App context
* @return True if connected to wifi
*/
private static boolean isWifiActive(Context context)
{
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
return manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}
}

View File

@@ -26,10 +26,13 @@ import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
@@ -100,11 +103,13 @@ public class NavigationHelper {
final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final boolean playbackSkipSilence,
@Nullable final String playbackQuality) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch)
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
}
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
@@ -281,9 +286,11 @@ public class NavigationHelper {
return fragmentManager.popBackStackImmediate(SEARCH_FRAGMENT_TAG, 0);
}
public static void openSearchFragment(FragmentManager fragmentManager, int serviceId, String query) {
public static void openSearchFragment(FragmentManager fragmentManager,
int serviceId,
String searchString) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query))
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, searchString))
.addToBackStack(SEARCH_FRAGMENT_TAG)
.commit();
}
@@ -312,7 +319,11 @@ public class NavigationHelper {
.commit();
}
public static void openChannelFragment(FragmentManager fragmentManager, int serviceId, String url, String name) {
public static void openChannelFragment(
FragmentManager fragmentManager,
int serviceId,
String url,
String name) {
if (name == null) name = "";
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name))
@@ -320,7 +331,10 @@ public class NavigationHelper {
.commit();
}
public static void openPlaylistFragment(FragmentManager fragmentManager, int serviceId, String url, String name) {
public static void openPlaylistFragment(FragmentManager fragmentManager,
int serviceId,
String url,
String name) {
if (name == null) name = "";
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name))
@@ -368,10 +382,10 @@ public class NavigationHelper {
// Through Intents
//////////////////////////////////////////////////////////////////////////*/
public static void openSearch(Context context, int serviceId, String query) {
public static void openSearch(Context context, int serviceId, String searchString) {
Intent mIntent = new Intent(context, MainActivity.class);
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
mIntent.putExtra(Constants.KEY_QUERY, query);
mIntent.putExtra(Constants.KEY_SEARCH_STRING, searchString);
mIntent.putExtra(Constants.KEY_OPEN_SEARCH, true);
context.startActivity(mIntent);
}
@@ -465,7 +479,8 @@ public class NavigationHelper {
switch (linkType) {
case STREAM:
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, PreferenceManager.getDefaultSharedPreferences(context)
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.autoplay_through_intent_key), false));
break;
}

View File

@@ -5,7 +5,6 @@ import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
@@ -31,6 +30,18 @@ public class ServiceHelper {
}
}
public static String getTranslatedFilterString(String filter, Context c) {
switch(filter) {
case "all": return c.getString(R.string.all);
case "videos": return c.getString(R.string.videos);
case "channels": return c.getString(R.string.channels);
case "playlists": return c.getString(R.string.playlists);
case "tracks": return c.getString(R.string.tracks);
case "users": return c.getString(R.string.users);
default: return filter;
}
}
/**
* Get a resource string with instructions for importing subscriptions for each service.
*

View File

@@ -62,7 +62,12 @@ public class ZipHelper {
* @return will return true if the file was found within the zip file
* @throws Exception
*/
public static boolean extractFileFromZip(ZipInputStream inZip, String file, String name) throws Exception {
public static boolean extractFileFromZip(String filePath, String file, String name) throws Exception {
ZipInputStream inZip = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(filePath)));
byte data[] = new byte[BUFFER_SIZE];
boolean found = false;
@@ -89,6 +94,6 @@ public class ZipHelper {
inZip.closeEntry();
}
}
return true;
return found;
}
}

View File

@@ -5,6 +5,7 @@ import android.os.Looper;
import android.util.Log;
import java.io.File;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -312,6 +313,13 @@ public class DownloadMission implements Serializable {
}
}
private void readObject(ObjectInputStream inputStream)
throws java.io.IOException, ClassNotFoundException
{
inputStream.defaultReadObject();
mListeners = new ArrayList<>();
}
private void deleteThisFromFile() {
new File(getMetaFilename()).delete();
}

View File

@@ -2,7 +2,6 @@ package us.shandian.giga.get;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -104,11 +103,11 @@ public class DownloadRunnable implements Runnable {
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(start);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
java.io.InputStream ipt = conn.getInputStream();
byte[] buf = new byte[64*1024];
while (start < end && mMission.running) {
int len = ipt.read(buf, 0, 512);
int len = ipt.read(buf, 0, buf.length);
if (len == -1) {
break;

View File

@@ -1,11 +1,14 @@
package us.shandian.giga.ui.adapter;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
@@ -22,9 +25,13 @@ import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.download.DeleteDownloadManager;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -46,20 +53,36 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
ALGORITHMS.put(R.id.sha1, "SHA1");
}
private Context mContext;
private Activity mContext;
private LayoutInflater mInflater;
private DownloadManager mManager;
private DownloadManager mDownloadManager;
private DeleteDownloadManager mDeleteDownloadManager;
private List<DownloadMission> mItemList;
private DownloadManagerService.DMBinder mBinder;
private int mLayout;
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager downloadManager, DeleteDownloadManager deleteDownloadManager, boolean isLinear) {
mContext = context;
mManager = manager;
mDownloadManager = downloadManager;
mDeleteDownloadManager = deleteDownloadManager;
mBinder = binder;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
mItemList = new ArrayList<>();
updateItemList();
}
public void updateItemList() {
mItemList.clear();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
DownloadMission mission = mDownloadManager.getMission(i);
if (!mDeleteDownloadManager.contains(mission)) {
mItemList.add(mDownloadManager.getMission(i));
}
}
}
@Override
@@ -98,7 +121,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
@Override
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
DownloadMission ms = mManager.getMission(pos);
DownloadMission ms = mItemList.get(pos);
h.mission = ms;
h.position = pos;
@@ -119,7 +142,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
@Override
public int getItemCount() {
return mManager.getCount();
return mItemList.size();
}
@Override
@@ -210,12 +233,12 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
int id = item.getItemId();
switch (id) {
case R.id.start:
mManager.resumeMission(h.position);
mBinder.onMissionAdded(mManager.getMission(h.position));
mDownloadManager.resumeMission(h.position);
mBinder.onMissionAdded(mItemList.get(h.position));
return true;
case R.id.pause:
mManager.pauseMission(h.position);
mBinder.onMissionRemoved(mManager.getMission(h.position));
mDownloadManager.pauseMission(h.position);
mBinder.onMissionRemoved(mItemList.get(h.position));
h.lastTimeStamp = -1;
h.lastDone = -1;
return true;
@@ -241,13 +264,14 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
return true;
case R.id.delete:
mManager.deleteMission(h.position);
mDeleteDownloadManager.add(h.mission);
updateItemList();
notifyDataSetChanged();
return true;
case R.id.md5:
case R.id.sha1:
DownloadMission mission = mManager.getMission(h.position);
new ChecksumTask().execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
DownloadMission mission = mItemList.get(h.position);
new ChecksumTask(mContext).execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
return true;
default:
return false;
@@ -258,19 +282,6 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
popup.show();
}
private void viewFile(File file, String mimetype) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
private void viewFileWithFileProvider(File file, String mimetype) {
String ourPackage = mContext.getApplicationContext().getPackageName();
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);
@@ -352,18 +363,26 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
}
private class ChecksumTask extends AsyncTask<String, Void, String> {
private static class ChecksumTask extends AsyncTask<String, Void, String> {
ProgressDialog prog;
WeakReference<Activity> weakReference;
ChecksumTask(@NonNull Activity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// Create dialog
prog = new ProgressDialog(mContext);
prog.setCancelable(false);
prog.setMessage(mContext.getString(R.string.msg_wait));
prog.show();
Activity activity = getActivity();
if (activity != null) {
// Create dialog
prog = new ProgressDialog(activity);
prog.setCancelable(false);
prog.setMessage(activity.getString(R.string.msg_wait));
prog.show();
}
}
@Override
@@ -374,8 +393,24 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
prog.dismiss();
Utility.copyToClipboard(mContext, result);
if (prog != null) {
Utility.copyToClipboard(prog.getContext(), result);
if (getActivity() != null) {
prog.dismiss();
}
}
}
@Nullable
private Activity getActivity() {
Activity activity = weakReference.get();
if (activity != null && activity.isFinishing()) {
return null;
} else {
return activity;
}
}
}
}

View File

@@ -10,6 +10,8 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -19,13 +21,15 @@ import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.download.DeleteDownloadManager;
import io.reactivex.disposables.Disposable;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.adapter.MissionAdapter;
public abstract class MissionsFragment extends Fragment {
private DownloadManager mManager;
private DownloadManager mDownloadManager;
private DownloadManagerService.DMBinder mBinder;
private SharedPreferences mPrefs;
@@ -37,14 +41,19 @@ public abstract class MissionsFragment extends Fragment {
private GridLayoutManager mGridManager;
private LinearLayoutManager mLinearManager;
private Context mActivity;
private DeleteDownloadManager mDeleteDownloadManager;
private Disposable mDeleteDisposable;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (DownloadManagerService.DMBinder) binder;
mManager = setupDownloadManager(mBinder);
updateList();
mDownloadManager = setupDownloadManager(mBinder);
if (mDeleteDownloadManager != null) {
mDeleteDownloadManager.setDownloadManager(mDownloadManager);
updateList();
}
}
@Override
@@ -55,6 +64,14 @@ public abstract class MissionsFragment extends Fragment {
};
public void setDeleteManager(@NonNull DeleteDownloadManager deleteDownloadManager) {
mDeleteDownloadManager = deleteDownloadManager;
if (mDownloadManager != null) {
mDeleteDownloadManager.setDownloadManager(mDownloadManager);
updateList();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.missions, container, false);
@@ -104,10 +121,26 @@ public abstract class MissionsFragment extends Fragment {
mActivity = activity;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mDeleteDownloadManager != null) {
mDeleteDisposable = mDeleteDownloadManager.getUndoObservable().subscribe(mission -> {
if (mAdapter != null) {
mAdapter.updateItemList();
mAdapter.notifyDataSetChanged();
}
});
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().unbindService(mConnection);
if (mDeleteDisposable != null) {
mDeleteDisposable.dispose();
}
}
@Override
@@ -129,7 +162,7 @@ public abstract class MissionsFragment extends Fragment {
}
private void updateList() {
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mDownloadManager, mDeleteDownloadManager, mLinear);
if (mLinear) {
mList.setLayoutManager(mLinearManager);
@@ -143,7 +176,7 @@ public abstract class MissionsFragment extends Fragment {
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
}
mPrefs.edit().putBoolean("linear", mLinear).commit();
mPrefs.edit().putBoolean("linear", mLinear).apply();
}
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);

View File

@@ -12,6 +12,7 @@ import android.widget.Toast;
import org.schabi.newpipe.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -59,17 +60,17 @@ public class Utility {
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName));
objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
objectOutputStream.writeObject(serializable);
} catch (Exception e) {
//nothing to do
}
if(objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (Exception e) {
//nothing to do
} finally {
if(objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (Exception e) {
//nothing to do
}
}
}
}

View File

@@ -5,7 +5,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@android:color/black"
android:gravity="center">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
@@ -42,7 +41,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:visibility="gone"
tools:background="@android:color/white"
tools:ignore="ContentDescription"

View File

@@ -260,13 +260,95 @@
</RelativeLayout>
<View
android:id="@+id/separatorCheckbox"
android:id="@+id/separatorStepSizeSelector"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/pitchControl"
android:layout_margin="@dimen/video_item_search_padding"
android:background="?attr/separator_color"/>
<LinearLayout
android:id="@+id/stepSizeSelector"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:layout_below="@id/separatorStepSizeSelector">
<TextView
android:id="@+id/stepSizeText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_step"
android:textStyle="bold"
android:clickable="false"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeOnePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeFivePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeTenPercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeTwentyFivePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeOneHundredPercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
</LinearLayout>
<View
android:id="@+id/separatorCheckbox"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/stepSizeSelector"
android:layout_margin="@dimen/video_item_search_padding"
android:background="?attr/separator_color"/>
<CheckBox
android:id="@+id/unhookCheckbox"
android:layout_width="match_parent"
@@ -279,33 +361,17 @@
android:layout_centerHorizontal="true"
android:layout_below="@id/separatorCheckbox"/>
<LinearLayout
android:id="@+id/presetSelector"
<CheckBox
android:id="@+id/skipSilenceCheckbox"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:layout_below="@id/unhookCheckbox">
<TextView
android:id="@+id/presetNightcore"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_nightcore"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/presetReset"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_default"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
</LinearLayout>
android:layout_height="wrap_content"
android:checked="false"
android:clickable="true"
android:focusable="true"
android:text="@string/skip_silence_checkbox"
android:maxLines="1"
android:layout_centerHorizontal="true"
android:layout_below="@id/unhookCheckbox"/>
<!-- END HERE -->

View File

@@ -49,7 +49,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/separator"
android:background="?android:windowBackground"
android:scrollbars="vertical"
android:visibility="gone"
tools:visibility="visible"

View File

@@ -111,5 +111,26 @@
android:layout_gravity="end"
android:text="@string/open_in_browser" />
<TextView
android:id="@+id/title_privacy_policy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:text="@string/privacy_policy_title"
android:textAppearance="@android:style/TextAppearance.Medium" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/privacy_policy_encouragement" />
<Button
android:id="@+id/privacy_policy_link"
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/read_privacy_policy" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>

View File

@@ -9,7 +9,6 @@
android:id="@+id/items_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:scrollbars="vertical"
tools:listitem="@layout/list_stream_item"/>

View File

@@ -9,7 +9,6 @@
android:id="@+id/items_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:scrollbars="vertical"
tools:listitem="@layout/list_stream_item"/>

View File

@@ -9,7 +9,6 @@
android:id="@+id/items_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:scrollbars="vertical"
tools:listitem="@layout/list_stream_mini_item"/>

View File

@@ -111,7 +111,7 @@
<TextView
android:id="@+id/resizeTextView"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="?attr/selectableItemBackground"

View File

@@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">
<RelativeLayout
@@ -12,7 +11,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true">
android:focusable="true"
android:background="?attr/selectableItemBackground">
<ImageView
android:id="@+id/sortButtonIcon"
android:layout_width="48dp"

View File

@@ -1,8 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="background_player_playing_toast">جاري التشغيل في الخلفية</string>
<string name="background_player_playing_toast">يتم التشغيل في الخلفية</string>
<string name="cancel">إلغاء</string>
<string name="choose_browser">إختر متصفح</string>
<string name="choose_browser">اختر المتصفح</string>
<string name="dark_theme_title">مظلم</string>
<string name="default_audio_format_title">صيغة الصوت الإفتراضية</string>
<string name="default_resolution_title">الدقة الإفتراضية</string>
@@ -10,26 +10,26 @@
<string name="detail_likes_img_view_description">الإعجابات</string>
<string name="detail_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="detail_uploader_thumbnail_view_description">الصورة المصغرة الشخصية</string>
<string name="did_you_mean">هل تقصد : %1$s ؟</string>
<string name="did_you_mean">هل تقصد: %1$s ?</string>
<string name="download">تنزيل</string>
<string name="download_dialog_title">تنزيل</string>
<string name="download_path_audio_dialog_title">أدخل مسار لتنزيل الملفات الصوتية.</string>
<string name="download_path_audio_dialog_title">أدخل مسار تنزيل الملفات الصوتية</string>
<string name="download_path_audio_summary">مسار حفظ التنزيلات الصوتية</string>
<string name="download_path_audio_title">مسار الصوتيات المُنزَّلة</string>
<string name="download_path_audio_title">مسار ملفات الصوت المحفوظة</string>
<string name="download_path_dialog_title">أدخل مسار التنزيل لملفات الفيديو</string>
<string name="download_path_summary">مسار حفظ تنزيلات الفيديو في</string>
<string name="download_path_title">مسار الفيديوهات المُنزّلَة</string>
<string name="download_path_title">مسار ملفات الفيديو المحفوظة</string>
<string name="err_dir_create">"لا يمكن إنشاء مجلد للتنزيلات في '%1$s'"</string>
<string name="info_dir_created">"تم إنشاء مجلد تنزيلات في '%1$s'"</string>
<string name="install">تثبيت</string>
<string name="kore_not_found">تطبيق Kore غير موجود. هل تريد تثبيته ؟</string>
<string name="light_theme_title">مضيء</string>
<string name="list_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="m4a_description">M4A — جودة أفضل</string>
<string name="m4a_description">جودة أفضل — M4A</string>
<string name="network_error">خطأ في الشبكة</string>
<string name="next_video_title">الفيديو التالي</string>
<string name="no_player_found">لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟</string>
<string name="open_in_browser">إفتح في متصفح</string>
<string name="open_in_browser">فتح في المتصفح</string>
<string name="play_audio">الصوت</string>
<string name="play_btn_text">تشغيل</string>
<string name="play_with_kodi_title">تشغيل بواسطة Kodi</string>
@@ -38,83 +38,83 @@
<string name="search_language_title">اللغة الإفتراضية للمحتوى</string>
<string name="settings">الإعدادات</string>
<string name="settings_category_appearance_title">المظهر</string>
<string name="settings_category_other_title">أخرئ</string>
<string name="settings_category_other_title">اخرى</string>
<string name="settings_category_video_audio_title">الفيديو والصوتيات</string>
<string name="share">مشاركة</string>
<string name="share_dialog_title">مشاركة بواسطة</string>
<string name="show_next_and_similar_title">عرض التالي والفيديوهات المشابهة</string>
<string name="show_next_and_similar_title">عرض الفديوهات \'التالية\'و\'المماثلة\'</string>
<string name="show_play_with_kodi_summary">عرض خيار لتشغيل الفيديو بواسطة Kodi Media Center</string>
<string name="show_play_with_kodi_title">عرض خيار التشغيل بواسطة Kodi</string>
<string name="theme_title">السمة</string>
<string name="upload_date_text">نُشِرت في %1$s</string>
<string name="upload_date_text">تم النشر يوم %1$s</string>
<string name="url_not_supported_toast">الرابط غير مدعوم</string>
<string name="use_external_audio_player_title">استخدام مشغل صوت خارجي</string>
<string name="use_external_video_player_title">استخدام مشغل فيديو خارجي</string>
<string name="use_tor_summary">(إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ).</string>
<string name="use_tor_title">استخدام Tor</string>
<string name="view_count_text">%1$s مشاهدات</string>
<string name="webm_description">WebM—صيغة حرة</string>
<string name="view_count_text">مشاهدات %1$s</string>
<string name="webm_description">تنسيق حر — WebM</string>
<string name="blocked_by_gema">تم حجبه بواسطة GEMA</string>
<string name="content_not_available">المحتوى غير متاح</string>
<string name="could_not_load_thumbnails">لم يتمكن من تحميل كل صور المعاينة</string>
<string name="general_error">خطأ</string>
<string name="parsing_error">تعذرت عملية تحليل الموقع</string>
<string name="youtube_signature_decryption_error">تعذر فك تشفير توقيع رابط الفيديو</string>
<string name="main_bg_subtitle">أنقر على البحث للبدء</string>
<string name="subscribe_button_title">إشتراك</string>
<string name="main_bg_subtitle">اضغط بحث للبدء</string>
<string name="subscribe_button_title">اشتراك</string>
<string name="subscribed_button_title">مشترك</string>
<string name="tab_main">الرئيسية</string>
<string name="tab_subscriptions">الإشتراكات</string>
<string name="tab_subscriptions">الاشتراكات</string>
<string name="fragment_whats_new">ما الجديد</string>
<string name="controls_background_title">الخلفية</string>
<string name="autoplay_by_calling_app_title">التشغيل التلقائي</string>
<string name="black_theme_title">أسوَد</string>
<string name="enable_watch_history_title">التاريخ</string>
<string name="settings_category_history_title">التأريخ و ذاكرة التخزين المؤقتة</string>
<string name="autoplay_by_calling_app_title">تشغيل تلقائي</string>
<string name="black_theme_title">اسود</string>
<string name="enable_watch_history_title">التاريخ وذاكرة التخزين المؤقت</string>
<string name="settings_category_history_title">التاريخ و ذاكرة التخزين المؤقتة</string>
<string name="content">المحتوى</string>
<string name="downloads">التنزيلات</string>
<string name="downloads_title">التنزيلات</string>
<string name="all">الكل</string>
<string name="channel">قناة</string>
<string name="channel">القناة</string>
<string name="video">فيديو</string>
<string name="settings_category_downloads_title">التنزيل</string>
<string name="action_about">عن التطبيق</string>
<string name="tab_about">عن التطبيق</string>
<string name="title_activity_history">التأريخ</string>
<string name="action_history">التأريخ</string>
<string name="open_in_popup_mode">إفتح في نافذة منبثقه</string>
<string name="use_external_video_player_summary">عند تمكين هذا الخيار لن يكون هنالك صوت في بعض خيارات الجودة</string>
<string name="popup_mode_share_menu_title">وضع نافذة NewPipe المنبثقة</string>
<string name="channel_unsubscribed">تم إلغاء اشتراك القناة</string>
<string name="subscription_change_failed">تعذر تغيير في الاشتراك</string>
<string name="title_activity_history">التاريخ</string>
<string name="action_history">التاريخ</string>
<string name="open_in_popup_mode">فتح في وضع النافذة المنبثقة</string>
<string name="use_external_video_player_summary">"بعض الخيارات الدقة لن تحتوي على صوت عند تمكين هذا الخيار "</string>
<string name="popup_mode_share_menu_title">وضع النوافذ المنبثقة NewPipe</string>
<string name="channel_unsubscribed">تم إلغاء الاشتراك في القناة</string>
<string name="subscription_change_failed">تعذر تغيير حالة الاشتراك</string>
<string name="subscription_update_failed">تعذر تحديث الاشتراك</string>
<string name="controls_popup_title">نافذة</string>
<string name="autoplay_by_calling_app_summary">تشغيل الفيديو تلقائيا عند استدعاء NewPipe من تطبيق آخر</string>
<string name="default_popup_resolution_title">الجودة الإفتراضية للنوافذ المنبثقة</string>
<string name="show_higher_resolutions_title">إظهار جودة أعلى</string>
<string name="show_higher_resolutions_summary">بعض الأجهزة فقط تدعم تشغيل فيديوهات 2K / 4K</string>
<string name="autoplay_by_calling_app_summary">تشغيل مقطع الفيديو عند إستدعاء NewPipe من تطبيق آخر</string>
<string name="default_popup_resolution_title">الدقة الافتراضية لنوافذ المنبثقة</string>
<string name="show_higher_resolutions_title">"عرض أعلى جودة "</string>
<string name="show_higher_resolutions_summary">بعض الأجهزة فقط تدعم تشغيل مقاطع الفيديو 2K / 4K</string>
<string name="default_video_format_title">تنسيق الفيديو الافتراضي</string>
<string name="popup_remember_size_pos_title">تذكر حجم النافذة و وضعها</string>
<string name="popup_remember_size_pos_summary">تذكر آخر حجم ومكان النافذة</string>
<string name="popup_remember_size_pos_summary">تذكر آخر مكان و حجم للنافذة المنبثقة</string>
<string name="player_gesture_controls_title">اعدادات إيماءة المشغل</string>
<string name="player_gesture_controls_summary">استخدم إيماءات للتحكم في سطوع وحجم المشغل</string>
<string name="player_gesture_controls_summary">استخدم إيماءات التحكم في سطوع وصوت المشغل</string>
<string name="show_search_suggestions_title">اقتراحات البحث</string>
<string name="show_search_suggestions_summary">عرض الاقتراحات عند البحث</string>
<string name="enable_search_history_title">سجل البحث</string>
<string name="enable_search_history_summary">تخزين طلبات البحث محليا</string>
<string name="enable_watch_history_summary">تتبع مقاطع الفيديو التي تمت مشاهدتها</string>
<string name="resume_on_audio_focus_gain_title">استئناف عند اكساب التركيز</string>
<string name="resume_on_audio_focus_gain_title">استئناف عند اكتساب التركيز</string>
<string name="resume_on_audio_focus_gain_summary">متابعة التشغيل بعد المقاطعات (مثل المكالمات الهاتفية)</string>
<string name="show_hold_to_append_title">إظهار تعليق لإلحاق تلميح</string>
<string name="show_hold_to_append_title">عرض\"عقد لإلحاق\"عنوان</string>
<string name="show_hold_to_append_summary">عرض تلميح أو زر منبثق عند الضغط على خلفية على صفحة تفاصيل الفيديو</string>
<string name="settings_category_player_title">المشغل</string>
<string name="settings_category_player_behavior_title">السلوك</string>
<string name="settings_category_popup_title">المنبثق</string>
<string name="popup_playing_toast">التشغيل في الوضع المنبثق</string>
<string name="popup_playing_toast">يتم التشغيل في الوضع المنبثق</string>
<string name="background_player_append">تم وضعه على قائمة الانتظار في مشغل الخلفية</string>
<string name="popup_playing_append">تم وضعه على قائمة الانتظار في مشغل النافذة المنبثقة</string>
<string name="show_age_restricted_content_title">عرض المحتوى المقيّد بحسب العُمر</string>
@@ -123,7 +123,7 @@
<string name="error_report_title">تقرير الخطأ</string>
<string name="playlist">قائمة التشغيل</string>
<string name="yes">نعم</string>
<string name="later">لاحقًا</string>
<string name="later">لاحقاً</string>
<string name="disabled">مُعطل</string>
<string name="filter">فلتر</string>
<string name="refresh">تحديث</string>
@@ -144,8 +144,8 @@
<string name="could_not_get_stream">يتعذر الحصول على أي بث</string>
<string name="could_not_load_image">تعذر تحميل الصورة</string>
<string name="app_ui_crash">تعطل التطبيق / واجهة المستخدم</string>
<string name="player_stream_failure">أخفق تشغيل هذا البث</string>
<string name="player_unrecoverable_failure">حدث خطأ المشغل غير قابل للاسترداد</string>
<string name="player_stream_failure">لا يمكن تشغيل هذا البث</string>
<string name="player_unrecoverable_failure">حدث خطأ للمشغل غير قابل للاسترداد</string>
<string name="player_recoverable_failure">استرداد المشغل من الخطأ</string>
<string name="sorry_string">عذرا، لا ينبغي أن يحدث ذلك.</string>
@@ -154,7 +154,7 @@
<string name="error_snackbar_action">تقرير</string>
<string name="what_device_headline">معلومات:</string>
<string name="what_happened_headline">ماذا حدث:</string>
<string name="info_labels">What:\\nRequest:\\nContent Lang:\\nService:\\nGMT Time:\\nPackage:\\nVersion:\\nOS version:</string>
<string name="info_labels">ماذا:\\nطلب:\\nيحتوى اللغة:\\nSالخدمات:\\nتوقيت غرينتش:\\nحزمة:\\nالإصدار:\\nOS إصدار نظام التشغيل:</string>
<string name="your_comment">تعليقك (باللغة الإنجليزية):</string>
<string name="error_details_headline">تفاصيل:</string>
@@ -170,9 +170,9 @@
<string name="use_old_player_title">استخدام المشغل القديم</string>
<string name="use_old_player_summary">المشغل القديم المدمج في إطار Mediaframework</string>
<string name="short_thousand">ك</string>
<string name="short_million">م</string>
<string name="short_billion">ب</string>
<string name="short_thousand">الف</string>
<string name="short_million">مليون</string>
<string name="short_billion">بليون</string>
<string name="no_subscribers">صفر لا تقم با الإختيار (في بعض اللغات) لأنها ليست \"حالة خاصة\" للأندرويد</string>
<plurals name="subscribers">
@@ -186,14 +186,14 @@
<string name="no_views">لاتوجد مشاهدات</string>
<string name="no_videos">لاتوجد فديوهات</string>
<string name="start">بداية</string>
<string name="start">تشغيل</string>
<string name="pause">إيقاف</string>
<string name="view">شغل</string>
<string name="delete">حذف</string>
<string name="checksum">التوقيع</string>
<string name="add">قطعة</string>
<string name="finish">حسنا</string>
<string name="add">مهمة جديدة</string>
<string name="finish">حسناً</string>
<string name="msg_name">اسم الملف</string>
<string name="msg_threads">العمليات</string>
@@ -224,34 +224,34 @@
<string name="action_open_website">فتح الموقع</string>
<string name="tab_contributors">المساهمون</string>
<string name="tab_licenses">التراخيص</string>
<string name="app_description">واجهة أمامية لليوتوب مجانية مفتوحة المصدر و خفيفة الوزن لنظام التشغيل أندرويد.</string>
<string name="app_description">بث مباشر على يوتيوب مجاني خفيف الوزن لنظام أندرويد.</string>
<string name="contribution_title">ساهم</string>
<string name="contribution_encouragement">إذا كانت لديك أفكار؛ أو ترجمة، أو تغييرات تخص التصميم، أو تنظيف و تحسين الشفرة البرمجية ، أو تعديلات عميقة عليها، فتذكر أنّ مساعدتك دائما موضع ترحيب. وكلما أتممنا شيئا كلما كان ذلك أفضل !</string>
<string name="view_on_github">عرض على GitHub</string>
<string name="donation_title">تبرع</string>
<string name="donation_encouragement">يتم تطوير NewPipe من قبل المتطوعين الذين يقضون وقت فراغهم لتقديم أفضل تجربة لك. الآن حان الوقت لإعطاء مرة أخرى للتأكد من المطورين لدينا يمكن أن تجعل NewPipe أكثر و أفضل بينما نتمتع بكوب من جافا!</string>
<string name="donation_encouragement">"يتم تطوير NewPipe من قبل المتطوعين الذين يقضون وقت فراغهم لتقديم أفضل تجربة لك. حان الوقت لرد المساعدة مع المطورين وجعل NewPipe أكثر و أفضل أثناء الاستمتاع بفنجان من القهوة "</string>
<string name="give_back">تبرع</string>
<string name="website_title">الموقع</string>
<string name="website_encouragement">للحصول على مزيد من المعلومات و آخر الأخبار حول NewPipe الرجاء زيارة موقعنا على الانترنت.</string>
<string name="website_encouragement">قم بزيارة موقع NewPipe لمزيد من المعلومات والأخبار.</string>
<string name="app_license_title">تراخيص NewPipe</string>
<string name="read_full_license">قراءة الترخيص</string>
<string name="title_history_search">البحث</string>
<string name="title_history_view">شاهد</string>
<string name="title_history_view">تمت مشاهدته</string>
<string name="history_disabled">تم تعطيل السجل</string>
<string name="history_empty">التاريخ فارغ</string>
<string name="history_cleared">تم تنظيف التأريخ</string>
<string name="history_cleared">تم مسح التاريخ</string>
<string name="item_deleted">تم حذف العنصر</string>
<string name="delete_item_search_history">هل تريد حذف هذا العنصر من سجل البحث؟</string>
<string name="main_page_content">المحتوى</string>
<string name="main_page_content">محتوى الشاشة الرئيسية</string>
<string name="blank_page_summary">صفحة فارغة</string>
<string name="subscription_page_summary">صفحة الاشتراك</string>
<string name="feed_page_summary">صفحة الخلاصة</string>
<string name="channel_page_summary">صفحة القناة</string>
<string name="select_a_channel">حدد قناة</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في القناة بعد</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في اي قناة بعد</string>
<string name="trending">الترند</string>
<string name="top_50">أفضل 50</string>
<string name="new_and_hot">جديد &amp; وساخن</string>
@@ -265,21 +265,21 @@
<string name="reCaptcha_title">تحدي ريكابتشا</string>
<string name="hold_to_append">اضغط للإدراج بقائمة الانتظار</string>
<plurals name="views">
<item quantity="zero">لم تُشاهَد</item>
<item quantity="zero">لاتوجد مشاهدة</item>
<item quantity="one">%s مشاهدة</item>
<item quantity="two">مشاهدتَين</item>
<item quantity="few">%s مشاهدات</item>
<item quantity="many">%s مشاهدات</item>
<item quantity="other">%s مشاهدات</item>
<item quantity="two">مشاهدتين</item>
<item quantity="few">%s مشاهدات كثيرة</item>
<item quantity="many">%s عدد المشاهدات</item>
<item quantity="other">%s أقصى مشاهدات</item>
</plurals>
<plurals name="videos">
<item quantity="zero">لا فيديو</item>
<item quantity="one">%s فيديو</item>
<item quantity="two">فيديوهتان</item>
<item quantity="few">%s فيديوهات</item>
<item quantity="many">%s فيديوهات</item>
<item quantity="other">%s فيديوهات</item>
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="two">%s اثنان</item>
<item quantity="few">%s قليل</item>
<item quantity="many">%s عدد كثير</item>
<item quantity="other">"%s أخرى "</item>
</plurals>
<string name="recaptcha_request_toast">إعادة طلب كلمة التحقق</string>
@@ -289,26 +289,26 @@
<string name="select_a_kiosk">حدد كشك</string>
<string name="kiosk">كشك</string>
<string name="enqueue_on_background">إدراج بقائمة الانتظار على خلفية</string>
<string name="enqueue_on_background">إدراج بقائمة الانتظار في مشغل الخلفية</string>
<string name="enqueue_on_popup">إدراج بقائمة الانتظار على المنبثقة</string>
<string name="start_here_on_background">ابدأ هنا على خلفية المصدر</string>
<string name="default_content_country_title">المحتوى الإفتراضي حسب البلد</string>
<string name="toggle_orientation">تغيير الإتجاه</string>
<string name="switch_to_background">الإنتقال إلى الخلفية</string>
<string name="switch_to_popup">الإنتقال إلى نافذة منبثقة</string>
<string name="switch_to_main">التحول إلى الرئيسية</string>
<string name="switch_to_background">الإنتقال إلى التشغيل في الخلفية</string>
<string name="switch_to_popup">الإنتقال إلى التشغيل في النافذة المنبثقة</string>
<string name="switch_to_main">الإنتقال إلى الرئيسية</string>
<string name="service_title">الخدمة</string>
<string name="drawer_open">فتح الدرج</string>
<string name="drawer_close">إغلاق الدرج</string>
<string name="always">دائمًا</string>
<string name="always">دائماً</string>
<string name="just_once">مرة واحدة فقط</string>
<string name="invalid_url_toast">العنوان خاطئ</string>
<string name="no_player_found_toast">لم يتم العثور على مشغل بث (يمكنك تثبيت VLC لتشغيله)</string>
<string name="no_player_found_toast">لم يتم العثور على مشغل الفديو (يمكنك تثبيت VLC لتشغيله)</string>
<string name="import_data_title">استيراد قاعدة البيانات</string>
<string name="export_data_title">تصدير قاعدة البيانات</string>
<string name="import_data_summary">"سيتجاوز سجل التاريخ والاشتراكات الحالية "</string>
<string name="import_data_summary">"سيقوم بالكتابة على سجل التاريخ والاشتراكات الحالية "</string>
<string name="export_data_summary">تصدير سجل, الاشتراكات وقوائم التشغيل.</string>
<string name="show_info">عرض المعلومات</string>
@@ -330,27 +330,27 @@
<string name="export_complete_toast">تمت عملية التصدير</string>
<string name="import_complete_toast">إكتَملَت عملية الإستيراد</string>
<string name="could_not_import_all_files">تنبيه : تعذرت عملية استيراد كافة الملفات.</string>
<string name="drawer_header_action_paceholder_text">سوف نقوم بإدماج شيء جديد هنا قريبا ;D</string>
<string name="drawer_header_action_paceholder_text">سوف يظهر شيء هنا قريبا ;D</string>
<string name="video_player">قارئ الفيديوهات</string>
<string name="always_ask_open_action">أُطلُب دائمًا</string>
<string name="video_player">مشغل الفديو</string>
<string name="always_ask_open_action">السؤال دائماً</string>
<string name="preferred_player_fetcher_notification_title">عملية تحميل المعلومات جارية</string>
<string name="preferred_player_fetcher_notification_message">جارٍ تحميل المحتوى المطلوب</string>
<string name="preferred_player_fetcher_notification_title">الحصول على المعلومات …</string>
<string name="preferred_player_fetcher_notification_message">تحميل المحتوى المطلوب</string>
<string name="create_playlist">إنشاء قائمة تشغيل جديدة</string>
<string name="delete_playlist">حذف قائمة التشغيل</string>
<string name="rename_playlist">"إعادة تسمية قائمة التشغيل "</string>
<string name="playlist_name_input">التسمية</string>
<string name="playlist_name_input">الأسم</string>
<string name="append_playlist">إضافة إلى قائمة تشغيل</string>
<string name="delete_playlist_prompt">هل تريد حذف قائمة التشغيل هذه ؟</string>
<string name="playlist_creation_success">تم إنشاء قائمة التشغيل بنجاح</string>
<string name="playlist_creation_success">قائمة التشغيل التي تم إنشاؤها</string>
<string name="playlist_add_stream_success">تمت إضافتها إلى قائمة التشغيل</string>
<string name="playlist_delete_failure">فشِلت عملية حذف قائمة التشغيل</string>
<string name="playlist_delete_failure">لا يمكن حذف قائمة التشغيل</string>
<string name="resize_fill">ملئ</string>
<string name="resize_zoom">تقريب</string>
<string name="resize_fill">ملئ الشاشة</string>
<string name="resize_zoom">تكبير</string>
<string name="caption_font_size_settings_title">حجم خط التسمية</string>
<string name="smaller_caption_font_size">أصغر خط</string>
@@ -359,12 +359,12 @@
<string name="live_sync">مُزامَنة</string>
<string name="controls_download_desc">تنزيل الملف المتدفق.</string>
<string name="tab_bookmarks">المؤشرات</string>
<string name="controls_download_desc">تنزيل ملف البث.</string>
<string name="tab_bookmarks">الإشارات مرجعية</string>
<string name="use_inexact_seek_title">استعمال التقديم السريع الغير دقيق</string>
<string name="use_inexact_seek_summary">"التقديم الغير دقيق يسمح للمشغل بالإطلاع الى الأماكن بشكل اسرع مع دقة اقل "</string>
<string name="download_thumbnail_title">تحميل الصور الصغيرة</string>
<string name="download_thumbnail_title">تحميل الصور المصغرة</string>
<string name="thumbnail_cache_wipe_complete_notice">تم إفراغ مساحة ذاكرة التخزين المؤقتة الخاصة بالصور</string>
<string name="file">الملف</string>
@@ -372,9 +372,9 @@
<string name="file_name_empty_error">لا يمكن ترك إسم الملف فارغا</string>
<string name="error_occurred_detail">طرأ هناك خطأ : %1$s</string>
<string name="no_valid_zip_file">ملف مضغوط غير صالح</string>
<string name="unbookmark_playlist">حذف الفاصلة المرجعية</string>
<string name="unbookmark_playlist">إزالة الإشارات المرجعية</string>
<string name="resize_fit">ملئ الشاشة</string>
<string name="resize_fit">تناسب مع الشاشة</string>
<string name="caption_auto_generated">توليد تلقائي</string>
<string name="import_export_title">إستيراد و تصدير</string>
@@ -386,7 +386,106 @@
<string name="export_ongoing">عملية التصدير جارية …</string>
<string name="import_file_title">إستيراد ملف</string>
<string name="import_soundcloud_instructions_hint">yourid, soundcloud.com/yourid</string>
<string name="import_soundcloud_instructions_hint">"معرفك , soundcloud.com/ الخاص بك "</string>
<string name="playback_default">إفتراضي</string>
<string name="download_thumbnail_summary">تعطيل إيقاف جميع الصور المصغرة من تحميل البيانات واستخدام الذاكرة وحفظها. سيؤدي التغيير هذا إلى محو-ذاكرة التخزين المؤقت في الذاكرة-وذاكرة على القرص.</string>
<string name="metadata_cache_wipe_title">امسح البيانات الوصفية المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_summary">إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_complete_notice">تم محو ذاكرة التخزين المؤقت للبيانات الوصفية</string>
<string name="auto_queue_title">وضع البث القادم تلقائيا في قائمة الإنتظار</string>
<string name="auto_queue_summary">رفض البث المشابه في حال كون البث السابق يعمل في حالة عدم التكرار.</string>
<string name="set_as_playlist_thumbnail">إضافة صورة مصغرة إلى قائمة التشغيل</string>
<string name="bookmark_playlist">قائمة التشغيل المخزنة</string>
<string name="playlist_thumbnail_change_success">تم تغيير الصورة المصغرة لقائمة التشغيل</string>
<string name="caption_none">بدون تسميات توضيحية</string>
<string name="caption_setting_title">تسميات توضيحية</string>
<string name="caption_setting_description">تعديل مشغل نص التسمية التوضيحية وأنماط الخلفية. يتطلب إعادة تشغيل التطبيق لتصبح التغييرات سارية المفعول.</string>
<string name="enable_leak_canary_title">تمكين LeakCanary</string>
<string name="enable_leak_canary_summary">قد تتسبب مراقبة تسرب الذاكرة في عدم استجابة التطبيق عند تفريغ السجلات</string>
<string name="enable_disposed_exceptions_title">تقرير الأخطاء خارج دورة الحياة</string>
<string name="enable_disposed_exceptions_summary">فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها</string>
<string name="clear_views_history_title">محو سجل المشاهدة</string>
<string name="clear_views_history_summary">احذف محفوظات الفديوهات التي تم تشغيلها.</string>
<string name="delete_view_history_alert">حذف سجل المشاهدة بالكامل.</string>
<string name="view_history_deleted">سجل المشاهدة محذوف.</string>
<string name="clear_search_history_title">محو سجل البحث</string>
<string name="clear_search_history_summary">يحذف تاريخ البحث عن الكلمات الرئيسية.</string>
<string name="delete_search_history_alert">حذف محفوظات البحث بالكامل.</string>
<string name="search_history_deleted">سجل البحث المحذوف.</string>
<string name="external_player_unsupported_link_type">المشغل الخارجي لا يدعم هذه الأنواع من الروابط</string>
<string name="invalid_source">مصدر ملف / مصدر غير صالح</string>
<string name="invalid_file">الملف غير موجود أو ليس لديك الإذن الكافي للقراءة أو الكتابة إليه</string>
<string name="no_streams_available_download">لا توجد تدفقات متاحة للتنزيل</string>
<string name="one_item_deleted">تم حذف عنصر واحد.</string>
<string name="toast_no_player">لم يتم تثبيت أي تطبيق لتشغيل هذا الملف</string>
<string name="app_license">NewPipe هو برنامج مفتوح المصدر حقوق متروكة: يمكنك استخدام الكود ودراسته وتحسينه بإرادتك. و على وجه التحديد يمكنك إعادة توزيعه / أو تعديله تحت شروط رخصة GNU العمومية والتي نشرتها مؤسسة البرمجيات الحرة، سواء الإصدار 3 من الرخصة، أو (باختيارك) أي إصدار لاحق.</string>
<string name="delete_stream_history_prompt">هل تريد حذف هذا العنصر من سجل المشاهدة؟</string>
<string name="delete_all_history_prompt">هل أنت متأكد من أنك تريد حذف كل العناصر من السجل؟</string>
<string name="title_last_played">آخر ما تم تشغيله</string>
<string name="title_most_played">الأكثر تشغيلا</string>
<string name="override_current_data">هذا سوف يتجاوز الإعدادت الحالية الخاصة بك.</string>
<string name="preferred_open_action_settings_title">طريقة \'التشغيل\' المفضلة</string>
<string name="preferred_open_action_settings_summary">"الإجراء الافتراضي عند فتح المحتوى — %s"</string>
<string name="background_player">مشغل الخلفية</string>
<string name="popup_player">المشغل المنبثق</string>
<string name="previous_export">التصدير السابق</string>
<string name="subscriptions_import_unsuccessful">تعذر استيراد الاشتراكات</string>
<string name="subscriptions_export_unsuccessful">لا يمكن تصدير الاشتراكات</string>
<string name="import_youtube_instructions">استيراد اشتراكات YouTube عن طريق تنزيل ملف التصدير:
\n
\n1. انتقل إلى عنوان URL هذا: %1$s
\n2. تسجيل الدخول عندما يطلب منك
\n3. يجب أن يبدأ التنزيل (وهذا ملف التصدير)</string>
<string name="import_soundcloud_instructions">قم باستيراد ملف تعريف SoundCloud عن طريق كتابة عنوان URL أو معرفك:
\n
\n1. تمكين \"وضع سطح المكتب\" في متصفح الويب (الموقع غير متاح للأجهزة المحمولة)
\n2. انتقل إلى عنوان URL هذا: %1$s
\n3. تسجيل الدخول عندما يطلب منك
\n4. انسخ عنوان URL للملف الشخصي الذي تمت إعادة توجيهك إليه.</string>
<string name="import_network_expensive_warning">ضع في اعتبارك أن هذه العملية يمكن أن تكون مكلفة اذا كنت تستخدم بيانات اشتراك انترنت.
\n
\nهل تريد الاستمرار؟</string>
<string name="playback_speed_control">ضوابط سرعة التشغيل</string>
<string name="playback_tempo">سرعة الأداء</string>
<string name="playback_pitch">تردد الصوت</string>
<string name="unhook_checkbox">نزع الإرتباط (قد يسبب تشويه)</string>
<string name="playback_nightcore">تعديل الايقاع Nightcore</string>
<string name="import_settings">هل تريد أيضا استيراد الإعدادات؟</string>
<string name="privacy_policy_title">"سياسة الخصوصية في NewPipe "</string>
<string name="privacy_policy_encouragement">يأخذ مشروع NewPipe خصوصيتك على محمل الجد. لذلك ، لا يجمع التطبيق أي بيانات دون موافقتك.
\nتوضح سياسة خصوصية NewPipe بالتفصيل البيانات التي يتم إرسالها وتخزينها عند إرسال تقرير الأعطال.</string>
<string name="read_privacy_policy">قراءة سياسة الخصوصية</string>
<string name="start_accept_privacy_policy">من أجل الامتثال للائحة الأوروبية العامة لحماية البيانات (GDPR) ، فإننا نلفت انتباهك إلى سياسة خصوصية NewPipe. يرجى قراءتها بعناية.
\nو يجب عليك قبولها لإرسال تقرير الأخطاء إلينا.</string>
<string name="accept">قبول</string>
<string name="decline">رفض</string>
<string name="limit_data_usage_none_description">لا حدود</string>
<string name="limit_mobile_data_usage_title">الحد من جودة الفيديو عند استخدام بيانات الهاتف المحمول</string>
<string name="skip_silence_checkbox">تسريع إلى الأمام أثناء الصمت</string>
<string name="playback_step">خطوة</string>
<string name="playback_reset">إعادة تعيين</string>
<string name="minimize_on_exit_title">تصغير عند تبديل التطبيق</string>
<string name="minimize_on_exit_summary">الإجراء عند التبديل إلى تطبيق آخر من مشغل الفيديو الرئيسي — %s</string>
<string name="minimize_on_exit_none_description">لاشيء</string>
<string name="minimize_on_exit_background_description">تصغير إلى مشغل الخلفية</string>
<string name="minimize_on_exit_popup_description">تصغير إلى مشغل منبثق</string>
</resources>

View File

@@ -1,14 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Натисни на търсене за да започнеш</string>
<resources><string name="main_bg_subtitle">Докоснете търсачката, за да започнете</string>
<string name="view_count_text">%1$s гледания</string>
<string name="upload_date_text">Публикувано на %1$s</string>
<string name="no_player_found">Не е намерен стрийм плейър. Желаете ли да инсталирате VLC?</string>
<string name="install">Инсталирай</string>
<string name="cancel">Затвори</string>
<string name="open_in_browser">Отвори в браузъра</string>
<string name="open_in_popup_mode">Отвори в подпрозорец</string>
<string name="cancel">Отмени</string>
<string name="open_in_browser">Отвори в браузър</string>
<string name="open_in_popup_mode">Отвори в прозорец</string>
<string name="share">Сподели</string>
<string name="download">Сваляне</string>
<string name="download">Изтегли</string>
<string name="search">Търси</string>
<string name="settings">Настройки</string>
<string name="did_you_mean">Може би имахте в предвид: %1$s ?</string>
@@ -16,81 +16,81 @@
<string name="choose_browser">Избери браузър</string>
<string name="screen_rotation">ориентация</string>
<string name="use_external_video_player_title">Използвай външен видео плейър</string>
<string name="use_external_video_player_summary">Някои резолюции няма да имат вграден аудио поток ако изберете тази опция</string>
<string name="use_external_video_player_summary">Някои резолюции няма да имат вграден аудио поток, ако тази опция е избрана</string>
<string name="use_external_audio_player_title">Използвай външен аудио плейър</string>
<string name="popup_mode_share_menu_title">Режим с подпрозорец на NewPipe</string>
<string name="popup_mode_share_menu_title">NewPipe в прозорец</string>
<string name="subscribe_button_title">Абониране</string>
<string name="subscribed_button_title">Абониран</string>
<string name="channel_unsubscribed">Премахнат абонамент за канала</string>
<string name="subscription_change_failed">Не мога да променя абонаментите</string>
<string name="subscription_update_failed">Не мога да обновя абонаментите</string>
<string name="subscription_change_failed">Неуспешна промяна на абонамента</string>
<string name="subscription_update_failed">Неуспешно обновление на абонамента</string>
<string name="tab_subscriptions">Абонаменти</string>
<string name="fragment_whats_new">Новости</string>
<string name="fragment_whats_new">Обновления</string>
<string name="controls_background_title">Фон</string>
<string name="controls_popup_title">Подпрозорец</string>
<string name="controls_background_title">Във фонов режим</string>
<string name="controls_popup_title">В прозорец</string>
<string name="download_path_title">Локация за запис на клиповете</string>
<string name="download_path_summary">Път за съхранение на свалени клипове</string>
<string name="download_path_dialog_title">Въведи път за съхранение на свалени клипове</string>
<string name="download_path_title">Директория за изтегляне на видео</string>
<string name="download_path_summary">Папка за съхранение на изтеглените видеота</string>
<string name="download_path_dialog_title">Въведете път за съхранение на изтеглените видеота</string>
<string name="download_path_audio_title">Локация за запис на аудио</string>
<string name="download_path_audio_summary">Път за съхранение на свалено аудио</string>
<string name="download_path_audio_dialog_title">Въведи път за съхранение на свалено аудио</string>
<string name="download_path_audio_title">Директория за изтегляне на аудио</string>
<string name="download_path_audio_summary">Папка за съхранение на изтеглено аудио</string>
<string name="download_path_audio_dialog_title">Въведете път за съхранение на изтеглено аудио</string>
<string name="autoplay_by_calling_app_title">Автоматично възпроизвеждане</string>
<string name="autoplay_by_calling_app_summary">Автоматично въпроизвеждане на видео, когато NewPipe е повикан от друго приложение</string>
<string name="autoplay_by_calling_app_summary">Въпроизвежда видео, когато NewPipe е повикан от друго приложение</string>
<string name="default_resolution_title">Резолюция по подразбиране</string>
<string name="default_popup_resolution_title">Резолюция по подразбиране на подпрозореца</string>
<string name="default_popup_resolution_title">Резолюция по подразбиране на прозореца</string>
<string name="show_higher_resolutions_title">Покажи по-високи резолюции</string>
<string name="show_higher_resolutions_summary">Само някои устройства поддържат възпроизвеждане на 2K/4K клипове</string>
<string name="play_with_kodi_title">Въпроизвеждане с Kodi</string>
<string name="kore_not_found">Приложението Kore не е намерено. Желаете ли да го инсталирате?</string>
<string name="show_play_with_kodi_title">Покажи опция \"Въпроизвеждане с Kodi\"</string>
<string name="show_play_with_kodi_summary">Показване на опция за въпроизвеждане на видео чрез Kodi media center</string>
<string name="kore_not_found">Приложението Kore не е намерено. Желаете ли да го инсталирате?</string>
<string name="show_play_with_kodi_title">Покажи Възпроизвеждане с Kodi</string>
<string name="show_play_with_kodi_summary">Показване на опция за възпроизвеждане на видео чрез Kodi media center</string>
<string name="play_audio">Аудио</string>
<string name="default_audio_format_title">Аудио формат по подразбиране</string>
<string name="default_video_format_title">Видео формат по подразбиране</string>
<string name="webm_description">WebM — свободен формат</string>
<string name="m4a_description">M4A — по-добро качество</string>
<string name="theme_title">Тема</string>
<string name="theme_title">Тема на външния вид</string>
<string name="light_theme_title">Светла</string>
<string name="dark_theme_title">Тъмна</string>
<string name="black_theme_title">Черна</string>
<string name="popup_remember_size_pos_title">Помни размера и позицията на подпрозореца</string>
<string name="popup_remember_size_pos_summary">Запомни последния размер и позиция на подпрозореца</string>
<string name="popup_remember_size_pos_title">Помни размера и позицията на прозореца</string>
<string name="popup_remember_size_pos_summary">Използвай размера и позицията на прозореца от предишния път</string>
<string name="player_gesture_controls_title">Контролиране на плейъра чрез жестове</string>
<string name="player_gesture_controls_summary">Използвай жестове за да контролираш яркостта и силата на звука на плейъра</string>
<string name="player_gesture_controls_summary">Позволи използване на жестове за контрол на яркостта и силата на звука на плейъра</string>
<string name="show_search_suggestions_title">Предложения за търсене</string>
<string name="show_search_suggestions_summary">Покажи предположения по време на търсене</string>
<string name="show_search_suggestions_summary">Показвай предложения за търсене</string>
<string name="enable_search_history_title">История на търсенията</string>
<string name="enable_search_history_summary">Съхранявай заявките за търсене локално</string>
<string name="enable_watch_history_title">История</string>
<string name="enable_watch_history_summary">Следи изгледаните клипове</string>
<string name="resume_on_audio_focus_gain_title">Поднови клип при възобновяване на фокуса</string>
<string name="resume_on_audio_focus_gain_summary">Продължава въпроизвеждането след прекъсване (например, телефонно обаждане)</string>
<string name="download_dialog_title">Свалени</string>
<string name="next_video_title">Следващ клип</string>
<string name="show_next_and_similar_title">Показвай следващия и подобни клипове</string>
<string name="show_hold_to_append_title">Показвай поле със съвет \"Задръж за добавяне\"</string>
<string name="url_not_supported_toast">Неподдържан URL</string>
<string name="enable_watch_history_title">История и кеш-памет</string>
<string name="enable_watch_history_summary">Запаметявай кои видеота са гледани</string>
<string name="resume_on_audio_focus_gain_title">Възобнови при връщане на фокус</string>
<string name="resume_on_audio_focus_gain_summary">Продължавай възпроизвеждането след прекъсване (например телефонно обаждане)</string>
<string name="download_dialog_title">Изтегли</string>
<string name="next_video_title">Следващо видео</string>
<string name="show_next_and_similar_title">Показвай следващ и подобни</string>
<string name="show_hold_to_append_title">Показвай съвет „задръж за добавяне</string>
<string name="url_not_supported_toast">Непознат URL</string>
<string name="search_language_title">Език на съдържанието по подразбиране</string>
<string name="settings_category_player_title">Плейър</string>
<string name="settings_category_player_behavior_title">Поведение</string>
<string name="settings_category_video_audio_title">Видео &amp; Аудио</string>
<string name="settings_category_history_title">История</string>
<string name="settings_category_popup_title">Подпрозорец</string>
<string name="settings_category_appearance_title">Вид</string>
<string name="settings_category_history_title">История и кеш-памет</string>
<string name="settings_category_popup_title">Нов прозорец</string>
<string name="settings_category_appearance_title">Външност</string>
<string name="settings_category_other_title">Други</string>
<string name="background_player_playing_toast">Въпроизвеждане във фонов режим</string>
<string name="popup_playing_toast">Въпроизвеждане в подпрозорец</string>
<string name="background_player_playing_toast">Възпроизвеждане във фонов режим</string>
<string name="popup_playing_toast">Възпроизвеждане в подпрозорец</string>
<string name="background_player_append">Включен в опашката на фоновия плейър</string>
<string name="popup_playing_append">Включен в опашката за възпроизвеждане в подпрозорец</string>
<string name="popup_playing_append">Включен в опашката в нов прозорец</string>
<string name="play_btn_text">Възпроизвеждане</string>
<string name="content">Съдържание</string>
<string name="show_age_restricted_content_title">Покажи съдържание с ограничение във възрастта</string>
<string name="show_age_restricted_content_title">Покажи съдържание за възрастни</string>
<string name="video_is_age_restricted">Съдържание за възрастни. Разрешаването на такова съдържание става от Настройки.</string>
<string name="duration_live">на живо</string>
<string name="downloads">Изтегляния</string>
@@ -111,17 +111,17 @@
<string name="play_all">Възпроизведи всички</string>
<string name="notification_channel_name">Известия от NewPipe</string>
<string name="notification_channel_description">Известия за фоновия плейър и плейъра на подпрозореца на NewPipe</string>
<string name="notification_channel_description">Известия за фоновия плейър и плейъра в отделен прозорец на NewPipe</string>
<string name="unknown_content">[Неизвестен]</string>
<string name="general_error">Грешка</string>
<string name="network_error">Проблем с мрежата</string>
<string name="could_not_load_thumbnails">Не мога да заредя всички миниатюри</string>
<string name="parsing_error">Не мога да интерпретирам правилно уебсайта</string>
<string name="light_parsing_error">Не мога да интерпретирам изцяло правилно уебсайта</string>
<string name="parsing_error">Неуспешно пресъздаване на уебсайта</string>
<string name="light_parsing_error">Не мога да пресъздам изцяло уебсайта</string>
<string name="content_not_available">Съдържанието не е налично</string>
<string name="blocked_by_gema">Блокирано от GEMA</string>
<string name="blocked_by_gema">Блокирано от GEMA</string>
<string name="could_not_setup_download_menu">Не мога да настроя меню за сваляне</string>
<string name="live_streams_not_supported">Това е предаване на живо, което все още не се поддържа.</string>
<string name="could_not_get_stream">Не мога да достъпя нито един поток</string>
@@ -131,7 +131,7 @@
<string name="player_unrecoverable_failure">Критичен проблем с плейъра</string>
<string name="player_recoverable_failure">Опит за възстановяване от възникналия проблем с плейъра</string>
<string name="sorry_string">Съжаляваме, но това не би трябвало да се случва.</string>
<string name="sorry_string">Съжаляваме, това не би трябвало да се случва.</string>
<string name="error_report_button_text">Докладвай за грешката чрез имейл</string>
<string name="error_snackbar_message">Съжаляваме, възникнаха някои грешки.</string>
<string name="error_snackbar_action">ДОКЛАД</string>
@@ -141,18 +141,18 @@
<string name="error_details_headline">Подробности:</string>
<string name="detail_thumbnail_view_description">Миниатюра на клипа</string>
<string name="detail_uploader_thumbnail_view_description">Миниатюра на аватаря на качилия клипа</string>
<string name="detail_thumbnail_view_description">Миниатюра на видео</string>
<string name="detail_uploader_thumbnail_view_description">Миниатюра на аватара на качилия видео</string>
<string name="detail_likes_img_view_description">Харесвания</string>
<string name="detail_dislikes_img_view_description">Нехаресвания</string>
<string name="use_tor_title">Използвай Tor</string>
<string name="use_tor_summary">(Експериментално) Пренасочвай трафика при сваляне към мрежата на Tor за по-високо ниво на поверителност (стриймването на клипове все още не се поддържа).</string>
<string name="use_tor_title">Използвай Tor</string>
<string name="use_tor_summary">(Експериментално) Пренасочвай трафика при сваляне към мрежата на Tor за по-високо ниво на поверителност (стриймването на видео все още не се поддържа).</string>
<string name="report_error">Докладвай за грешка</string>
<string name="search_no_results">Няма резултати</string>
<string name="empty_subscription_feed_subtitle">Тук все още няма нищо</string>
<string name="empty_subscription_feed_subtitle">Тук няма нищо</string>
<string name="err_dir_create">Не мога да създам директория за свалянията в \'%1$s\'</string>
<string name="info_dir_created">Създадох директория за свалянията в \'%1$s\'</string>
<string name="err_dir_create">Неуспешно създаване на директория за изтеглени в %1$s</string>
<string name="info_dir_created">Бе създадена директория за свалянията в %1$s</string>
<string name="video">Видео</string>
<string name="audio">Аудио</string>
@@ -174,41 +174,287 @@
<string name="no_videos">Няма клипове</string>
<plurals name="videos">
<item quantity="one">%s клип</item>
<item quantity="other">%s клипове</item>
<item quantity="one">%s видео</item>
<item quantity="other">%s видеота</item>
</plurals>
<string name="pause">Пауза</string>
<string name="view">Начало</string>
<string name="delete">Изтрий</string>
<string name="checksum">Чексума</string>
<string name="checksum">Контролна сума</string>
<string name="finish">Готово</string>
<string name="finish">ОК</string>
<string name="msg_name">Име на файла</string>
<string name="msg_threads">Нишки</string>
<string name="msg_error">Грешка</string>
<string name="msg_server_unsupported">Сървърът не се поддържа</string>
<string name="msg_exists">Файлът вече съществува</string>
<string name="msg_url_malform">Неправилен формат на URL-а или няма връзка</string>
<string name="msg_running">NewPipe Сваляне</string>
<string name="msg_running_detail">Натисни за подробности</string>
<string name="msg_url_malform">Неправилен хиперлинк или няма връзка с Интернет</string>
<string name="msg_running">NewPipe Изтегляне</string>
<string name="msg_running_detail">Докосни за подробности</string>
<string name="msg_wait">Моля, изчакайте…</string>
<string name="msg_copied">Копирано в клипборда</string>
<string name="settings_file_charset_title">Позволени символи в името на файловете</string>
<string name="settings_file_replacement_character_summary">Невалидните символи се заменят с тази стойност</string>
<string name="settings_file_replacement_character_title">Стойност за замяна</string>
<string name="settings_file_replacement_character_title">Символ за замяна</string>
<string name="charset_letters_and_digits">Букви и цифри</string>
<string name="title_activity_about">За NewPipe</string>
<string name="action_settings">Настройки</string>
<string name="action_about">Относно приложението</string>
<string name="title_licenses">Лицензи от трети страни</string>
<string name="error_unable_to_load_license">Не мога да заредя лицензите</string>
<string name="error_unable_to_load_license">Неуспешно зареждане на лиценза</string>
<string name="action_open_website">Отвори уебсайта</string>
<string name="tab_about">Относно приложението</string>
<string name="tab_contributors">Допринесли</string>
<string name="tab_licenses">Лицензии</string>
<string name="app_description">Безплатен и лек YouTube фронтенд за Android.</string>
<string name="tab_licenses">Лицензи</string>
<string name="app_description">Безплатно и леко поточно предаване за Android.</string>
<string name="view_on_github">Виж в GitHub</string>
</resources>
<string name="controls_download_desc">Изтегли стрийм файл.</string>
<string name="show_info">Покажи инфо</string>
<string name="tab_main">Главен</string>
<string name="tab_bookmarks">Отметки</string>
<string name="controls_add_to_playlist_title">Добави към</string>
<string name="use_inexact_seek_title">Използвай бързо, но неточно превъртане</string>
<string name="use_inexact_seek_summary">Неточното превъртане позволява на плейъра да превърта кадри по-бързо, с намалена прецизност</string>
<string name="download_thumbnail_title">Зареждай миниатюри</string>
<string name="thumbnail_cache_wipe_complete_notice">Кеш-паметта с изображения е изтрита</string>
<string name="metadata_cache_wipe_title">Изтрий кешираните метаданни</string>
<string name="metadata_cache_wipe_summary">Премахни всички метаданни за уебстраници от кеш-паметта</string>
<string name="metadata_cache_wipe_complete_notice">Кеш-паметта с метаданни бе изтрита</string>
<string name="auto_queue_title">Автоматично нареди на опашка следващия</string>
<string name="auto_queue_summary">"Автоматично прибавяне на сродно съдържание при неповтарящ се преглед . "</string>
<string name="default_content_country_title">Държава, за която да бъде показвано съдържание</string>
<string name="service_title">Услуга</string>
<string name="settings_category_debug_title">Отстраняване на грешки</string>
<string name="always">Винаги</string>
<string name="just_once">Само веднъж</string>
<string name="file">Файл</string>
<string name="toggle_orientation">Смени ориентацията</string>
<string name="switch_to_background">Мини във фонов режим</string>
<string name="switch_to_popup">Мини към нов прозорец</string>
<string name="switch_to_main">Мини в основен режим</string>
<string name="import_data_title">Импортиране на база данни</string>
<string name="export_data_title">Експортиране на база данни</string>
<string name="import_data_summary">Ще замени текущите история и абонаменти</string>
<string name="export_data_summary">Експортиране на историята, абонаментите и плейлистите</string>
<string name="clear_views_history_title">Изтрий историята с изгледани</string>
<string name="delete_view_history_alert">Изтрий цялата история с изгледани.</string>
<string name="view_history_deleted">Историята с изгледани е изтрита.</string>
<string name="clear_search_history_title">Изтрий историята на търсенията</string>
<string name="clear_search_history_summary">Изтрива историята с въвежданите за търсене ключови думи.</string>
<string name="delete_search_history_alert">Изтрий цялата история на търсенията.</string>
<string name="search_history_deleted">Историята на търсенията е изтрита.</string>
<string name="youtube_signature_decryption_error">URL подписът на видеото не можа да бъде дешифрован</string>
<string name="external_player_unsupported_link_type">Външните плейъри не поддържат този вид линкове</string>
<string name="invalid_url_toast">Невалиден URL</string>
<string name="invalid_directory">Невалидна директория</string>
<string name="invalid_source">Невалиден файл или източник на съдържание</string>
<string name="invalid_file">Файлът не съществува или липсва разрешение за четене и/или запис</string>
<string name="file_name_empty_error">Името на файла не може да бъде празно</string>
<string name="error_occurred_detail">Възникна грешка: %1$s</string>
<string name="no_streams_available_download">Не са налични източници за изтегляне</string>
<string name="short_thousand">хил.</string>
<string name="short_million">млн.</string>
<string name="short_billion">млрд.</string>
<string name="no_subscribers">Няма абонати</string>
<string name="create">Създай</string>
<string name="delete_all">Изтрий всички</string>
<string name="dismiss">Откажи</string>
<string name="toast_no_player">Няма инсталирано приложение, което да изпълни този файл</string>
<string name="copyright" formatted="true">© %1$s от %2$s под лиценза %3$s</string>
<string name="contribution_title">Съдействайте</string>
<string name="contribution_encouragement">За всичко, което се сетите: превод, промени по дизайна, изчистване на кода или много сериозни промени по кода помощта е винаги добре дошла. Колкото повече развитие, толкова по-добре!</string>
<string name="donation_title">Направете дарение</string>
<string name="donation_encouragement">NewPipe се разработва от доброволци, които отделят от своето време, за да ви доставят най-доброто преживяване. Дайте от себе си в замяна, за да помогнете на разработчиците да направят NewPipe още по-добро приложение, докато се наслаждават на едно кафе от вас.</string>
<string name="give_back">Дари</string>
<string name="website_title">Уебсайт</string>
<string name="website_encouragement">Посетете сайта на NewPipe за повече информация и новини.</string>
<string name="privacy_policy_title">Политиката на NewPipe за личните данни</string>
<string name="privacy_policy_encouragement">Проектът NewPipe се отнася много сериозно към вашата поверителност. За това, приложението не събира никакви данни без вашето съгласие.
\nНашата политика за личните данни обяснява подробно какви данни изпращате и къде се съхраняват, когато изпращате съобщения за грешки.</string>
<string name="read_privacy_policy">Прочетете нашата политика за поверителност</string>
<string name="app_license_title">Лицензът на NewPipe</string>
<string name="no_player_found_toast">Липсва стрийм плейър (можете да изтеглите VLC, за да пуснете стрийма)</string>
<string name="download_thumbnail_summary">Изключете, за да спрете зареждането на всички миниатюри, спестявайки трафик и памет. При промяна на тази настройка, текущата кеш-памет на изображенията ще бъде изтрита.</string>
<string name="show_hold_to_append_summary">Показвай подсказка, когато е избран фонов режим или режим в прозорец на страницата с детайли на съответното видео</string>
<string name="clear_views_history_summary">Изтрива историята на възпроизвежданите стриймове.</string>
<string name="video_streams_empty">Не са намерени видео стриймове</string>
<string name="audio_streams_empty">Не са намерени аудио стриймове</string>
<string name="info_labels">"Какво:\\nЗаявка:\\nЕзик на съдържанието:\\nУслуга:\\nВреме по GMT:\\nПакет:\\nВерсия:\\nОС версия: "</string>
<string name="list_thumbnail_view_description">Миниатюра на видео</string>
<string name="user_report">Потребителски доклад</string>
<string name="detail_drag_description">Пренареди чрез плъзгане</string>
<string name="start">Начало</string>
<string name="delete_one">Изтрий един</string>
<string name="rename">Преименувай</string>
<string name="add">Нова цел</string>
<string name="no_available_dir">Моля, изберете достъпна папка за изтегляния</string>
<string name="msg_popup_permission">Това разрешение се изисква за
\nвъзпроизвеждане в отделен прозорец</string>
<string name="one_item_deleted">1 елемент е изтрит.</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA заявка</string>
<string name="recaptcha_request_toast">Изисква се въвеждане на reCAPTCHA</string>
<string name="settings_category_downloads_title">Изтегляне</string>
<string name="charset_most_special_characters">Повечето специални символи</string>
<string name="app_license">NewPipe е безплатен „copyleft“ софтуер: Можете да го използвате, изучавате, споделяте и подобрявате по ваше усмотрение. В частност, Вие можете да препубликувате и/или модифицирате приложението според правилата на Главния обществен лиценз на ГНУ, издаден от Фондацията за свободен софтуер версия 3 на лиценза или по-нова.</string>
<string name="read_full_license">Прочетете лиценза</string>
<string name="title_activity_history">История</string>
<string name="title_history_search">Търсения</string>
<string name="title_history_view">Гледани</string>
<string name="history_disabled">Историята е изключена</string>
<string name="action_history">История</string>
<string name="history_empty">Историята е празна</string>
<string name="history_cleared">Историята е изчистена</string>
<string name="item_deleted">Елементът е изтрит</string>
<string name="delete_item_search_history">Искате ли да изтриете този елемент от историята на търсенията?</string>
<string name="delete_stream_history_prompt">Искате ли да изтриете този елемент от историята на гледанията?</string>
<string name="delete_all_history_prompt">Окончателно ли искате да изтриете всички елементи от историята на гледанията?</string>
<string name="title_last_played">Последно възпроизвеждани</string>
<string name="title_most_played">Най-възпроизвеждани</string>
<string name="main_page_content">Съдържание на главната страница</string>
<string name="blank_page_summary">Празна страница</string>
<string name="kiosk_page_summary">Страница-павилион</string>
<string name="subscription_page_summary">Страница с абонаменти</string>
<string name="feed_page_summary">Страница с акценти от вашите абонаменти</string>
<string name="channel_page_summary">Страница на определен канал</string>
<string name="select_a_channel">Изберете канал</string>
<string name="no_channel_subscribed_yet">За момента нямате абонаменти</string>
<string name="select_a_kiosk">Изберете павилион</string>
<string name="export_complete_toast">Експортирането приключи</string>
<string name="import_complete_toast">Импортирането приключи</string>
<string name="no_valid_zip_file">Невалиден ZIP файл</string>
<string name="could_not_import_all_files">Внимание: не всички файлове бяха импортирани успешно.</string>
<string name="override_current_data">Това ще замени текущата Ви инсталация.</string>
<string name="import_settings">Желаете ли също да импортирате настройките?</string>
<string name="kiosk">Павилион</string>
<string name="trending">Набиращи популярност</string>
<string name="top_50">Топ 50</string>
<string name="new_and_hot">Ново и горещо</string>
<string name="title_activity_background_player">Във фонов режим</string>
<string name="title_activity_popup_player">В прозорец</string>
<string name="play_queue_remove">Премахни</string>
<string name="play_queue_stream_detail">Детайли</string>
<string name="play_queue_audio_settings">Аудио настройки</string>
<string name="hold_to_append">Задръжте, за да поставите на опашката</string>
<string name="enqueue_on_background">На опашката във „фонов режим“</string>
<string name="enqueue_on_popup">На опашката в „режим в прозорец“</string>
<string name="start_here_on_main">Възпроизвеждане от тук</string>
<string name="start_here_on_background">Възпроизвеждане от тук във фонов режим</string>
<string name="start_here_on_popup">Възпроизвеждане от тук в прозорец</string>
<string name="drawer_open">Отвори навигационната лента</string>
<string name="drawer_close">Затвори навигационната лента</string>
<string name="drawer_header_action_paceholder_text">Тук нещо ще се появи скоро ;D</string>
<string name="preferred_open_action_settings_title">Действие при повикване от друго приложение</string>
<string name="preferred_open_action_settings_summary">Действие по подразбиране при отваряне на съдържание — %s</string>
<string name="video_player">Видео плейър</string>
<string name="background_player">Във фонов режим</string>
<string name="popup_player">В прозорец</string>
<string name="always_ask_open_action">Винаги питай</string>
<string name="preferred_player_fetcher_notification_title">Получаване на инфо…</string>
<string name="preferred_player_fetcher_notification_message">Зареждане на заявеното съдържание</string>
<string name="create_playlist">Създай Нов Плейлист</string>
<string name="delete_playlist">Изтрий Плейлист</string>
<string name="rename_playlist">Преименувай Прелист</string>
<string name="playlist_name_input">Име</string>
<string name="append_playlist">Добави Към Плейлист</string>
<string name="set_as_playlist_thumbnail">Задай като миниатюра на плейлиста</string>
<string name="bookmark_playlist">Миниатюрата на плейлиста е сменена</string>
<string name="unbookmark_playlist">Премахни отметката</string>
<string name="delete_playlist_prompt">Искате ли да изтриете този плейлист?</string>
<string name="playlist_creation_success">Плейлистът е създаден</string>
<string name="playlist_add_stream_success">Добавено към плейлист</string>
<string name="playlist_thumbnail_change_success">Миниатюрата на плейлиста е сменена</string>
<string name="playlist_delete_failure">Плейлистът не можа да бъде изтрит</string>
<string name="caption_none">Без надписи</string>
<string name="resize_fit">Приспособи</string>
<string name="resize_fill">Запълни</string>
<string name="resize_zoom">Увеличи</string>
<string name="caption_auto_generated">Авто-генерирани</string>
<string name="caption_setting_title">Надписи</string>
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените.</string>
<string name="enable_leak_canary_title">Включи LeakCanary</string>
<string name="enable_leak_canary_summary">Следенето за пропускане на памет може да направи приложението нестабилно</string>
<string name="enable_disposed_exceptions_title">Докладвай за извънредни грешки</string>
<string name="import_export_title">Импортиране/експортиране</string>
<string name="import_title">Импортирай</string>
<string name="import_from">Импортирай от</string>
<string name="export_to">Експортирай в</string>
<string name="import_ongoing">Импортиране…</string>
<string name="export_ongoing">Експортиране…</string>
<string name="import_file_title">Файл с данни за импортиране</string>
<string name="previous_export">Предишно експортиране</string>
<string name="subscriptions_import_unsuccessful">Неуспешно импортиране на абонатите</string>
<string name="subscriptions_export_unsuccessful">Неуспешно експортиране на абонатите</string>
<string name="import_youtube_instructions">Импортирайте YouTube абонаментите, чрез изтегляне на нужния файл:
\n
\n1. Посетете тази връзка: %1$s
\n2. Влезте в акаунта си, когато това се изиска
\n3. Изтеглянето трябва да започне (това е експортирания файл)</string>
<string name="import_soundcloud_instructions">Импортирайте SoundCloud профил чрез въвеждане на хипервръзката към него или чрез вашия ID:
\n
\n1. Включете „десктоп режим“ в браузър (сайтът е недостъпен за мобилни устройства)
\n2. Посетете връзката: %1$s
\n3. Влезте в профила си, ако се изисква
\n4. Копирайте хипервръзката на профилната страница, към която сте насочени.</string>
<string name="import_soundcloud_instructions_hint">вашиятID, soundcloud.com/вашиятID</string>
<string name="import_network_expensive_warning">Това действие може да изразходва голямо количество данни от вашия трафик.
\n
\nЖелаете ли да продължите?</string>
<string name="playback_speed_control">Управление скоростта на възпроизвеждане</string>
<string name="playback_tempo">Темпо</string>
<string name="playback_pitch">Височина</string>
<string name="skip_silence_checkbox">Бързо превъртане при тишина</string>
<string name="start_accept_privacy_policy">От съображения към Общия европейски регламент относно защитата на данните, Ви привличаме вниманието към политиката за поверителност на NewPipe. Моля, прочетете я внимателно.
\nТрябва да сте съгласни с условията, за да ни изпратите доклада за грешката.</string>
<string name="accept">Приеми</string>
<string name="decline">Откажи</string>
<string name="limit_data_usage_none_description">Без ограничения</string>
<string name="limit_mobile_data_usage_title">Ограничена резолюция при мобилни данни</string>
<string name="minimize_on_exit_title">Минимизирай при преход към друго приложение</string>
<string name="minimize_on_exit_summary">Действие при преминаване към друго приложение от видео плейъра — %s</string>
<string name="minimize_on_exit_none_description">Без минимизиране</string>
<string name="minimize_on_exit_background_description">Минимизирай във фонов режим</string>
<string name="minimize_on_exit_popup_description">Минимизирай в прозорец</string>
</resources>

View File

@@ -5,15 +5,15 @@
<string name="upload_date_text">প্রকাশকাল %1$s</string>
<string name="no_player_found">কোন স্ট্রিম প্লেয়ার পাওয়া যায়নি। তুমি কি VLC ইনস্টল করতে চাও?</string>
<string name="install">ইনস্টল</string>
<string name="cancel">বাদ দাও</string>
<string name="cancel">"বাদ দিন "</string>
<!-- <string name="fdroid_vlc_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string> -->
<string name="open_in_browser">ব্রাউজারে খোলো</string>
<string name="open_in_popup_mode">পপআপ মোডে খোলো</string>
<string name="open_in_browser">"ব্রাউজারে ওপেন করো "</string>
<string name="open_in_popup_mode">"popup মোডে Open করো "</string>
<string name="share">শেয়ার</string>
<string name="download">ডাউনলোড</string>
<string name="search">খোঁজ</string>
<string name="settings">সেটিং</string>
<string name="did_you_mean">তুমি কি বলতে চাচ্ছ %1$s ?</string>
<string name="settings">"সেটিংস "</string>
<string name="did_you_mean">আপনি কি বুঝিয়েছেনঃ %1$s ?</string>
<string name="share_dialog_title">শেয়ার কর</string>
<string name="choose_browser">ব্রাউজার পছন্দ কর</string>
<string name="screen_rotation">রোটেশন</string>
@@ -25,12 +25,12 @@
<string name="controls_popup_title">পপআপ</string>
<string name="download_path_title">ভিডিও ডাউনলোড করার পাথ</string>
<string name="download_path_summary">ডাউনলোড করা ভিডিও সঞ্চয় করার পাথ।</string>
<string name="download_path_summary">ডাউনলোড করা ভিডিওগুলো রাখার ফোল্ডার</string>
<string name="download_path_dialog_title">ভিডিওগুলির জন্য ডাউনলোডের পাথ প্রবেশ করাও</string>
<string name="download_path_audio_title">অডিও ডাউনলোড পাথ</string>
<string name="download_path_audio_summary">ডাউনলোড করা অডিও সঞ্চয় করার পাথ</string>
<string name="download_path_audio_dialog_title">অডিও ফাইলগুলির জন্য ডাউনলোডের পাথ প্রবেশ করাও</string>
<string name="download_path_audio_dialog_title">অডিও ফাইলগুলির জন্য ডাউনলোডের পাথ প্রবেশ করাও</string>
<string name="autoplay_by_calling_app_title">স্বয়ংক্রিয়ভাবে প্লে করো যখন অন্য অ্যাপ্লিকেশন থেকে চালু করা হয়</string>
<string name="autoplay_by_calling_app_summary">স্বয়ংক্রিয়ভাবে একটি ভিডিও প্লে করো যখন NewPipe অন্য অ্যাপ্লিকেশন থেকে চালু করা হয়।</string>
@@ -42,7 +42,7 @@
<string name="kore_not_found">Kore অ্যাপ্লিকেশন খুঁজে পাওয়া যায়নি। Kore ইনস্টল করবে?</string>
<!-- <string name="fdroid_kore_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=Kore&amp;fdid=org.xbmc.kore</string> -->
<string name="show_play_with_kodi_title">দেখাও \"Kodi এর মাধ্যমে চালাও \" বিকল্প</string>
<string name="show_play_with_kodi_summary">Kodi মিডিয়া সেন্টারে এর মাধ্যমে ভিডিও প্লে করার জন্য একটি বিকল্প প্রদর্শন কর</string>
<string name="show_play_with_kodi_summary">Kodi মিডিয়া সেন্টারে এর মাধ্যমে ভিডিও প্লে করার জন্য একটি বিকল্প প্রদর্শন কর</string>
<string name="play_audio">অডিও</string>
<string name="default_audio_format_title">ডিফল্ট অডিও ফরম্যাট</string>
<string name="default_video_format_title">পছন্দসই ভিডিও ফরম্যাট</string>
@@ -200,4 +200,16 @@
<string name="action_settings">সেটিংস</string>
<string name="action_open_website">ওয়েব সাইট খুলুন</string>
<string name="website_title">ওয়েব সাইট</string>
<string name="no_player_found_toast">"কোন স্ট্রিম প্লেয়ার পাওয়া যায়নি (প্লে করতে VLC ইন্সটল করতে পারেন) "</string>
<string name="use_external_video_player_summary">যদি এই অপশন অন থাকে তবে কিছু ভিডিওর অডিও কাজ নাও করতে পারে</string>
<string name="subscribe_button_title">সাবস্ক্রাইব</string>
<string name="subscribed_button_title">সাবস্ক্রাইব করা আছে</string>
<string name="channel_unsubscribed">চ্যানেল সাবস্ক্রাইব করা হয়েছে</string>
<string name="subscription_change_failed">সাবস্ক্রিপশন পরিবর্তন করা হয়নি</string>
<string name="subscription_update_failed">সাবস্ক্রিপশন আপডেট করতে ব্যার্থ হয়েছে</string>
<string name="tab_main">প্রধান</string>
<string name="tab_subscriptions">সাবস্ক্রিপশন</string>
<string name="tab_bookmarks">বুকমার্কস</string>
<string name="use_inexact_seek_title">দ্রুত টানা ব্যাবহার করুন</string>
</resources>

View File

@@ -128,7 +128,7 @@
<string name="import_file_title">Importa un fitxer</string>
<string name="playback_default">Per defecte</string>
<string name="view_count_text">%1$s visualitzacions</string>
<string name="view_count_text">%1$s reproduccions</string>
<string name="upload_date_text">Publicat el %1$s</string>
<string name="no_player_found">No s\'ha trobat un reproductor de fluxos. Voleu instal·lar el VLC?</string>
<string name="no_player_found_toast">No s\'ha trobat cap reproductor de fluxos (podeu instal·lar el VLC per reproduir-lo)</string>
@@ -240,10 +240,10 @@
<string name="use_old_player_summary">Antic reproductor integrat de Mediaframework</string>
<string name="no_subscribers">Sense subscriptors</string>
<string name="no_views">Sense visualitzacions</string>
<string name="no_views">Sense reproduccions</string>
<plurals name="views">
<item quantity="one">%s visualització</item>
<item quantity="other">%s visualitzacions</item>
<item quantity="one">%s reproducció</item>
<item quantity="other">%s reproduccions</item>
</plurals>
<string name="no_videos">Sense vídeos</string>
@@ -354,7 +354,7 @@
<string name="donation_encouragement">El NewPipe està desenvolupat per voluntaris que fan servir el seu temps lliure per a oferir-vos la millor experiència possible. Feu una aportació per assegurar que els nostres desenvolupadors puguin millorar encara més el NewPipe mentre fan un cafè.</string>
<string name="give_back">Fes la teva aportació</string>
<string name="title_history_search">Cerques</string>
<string name="title_history_view">Visualitzacions</string>
<string name="title_history_view">Reproduccions</string>
<string name="no_channel_subscribed_yet">Encara no us heu subscrit a cap canal</string>
<string name="new_and_hot">Novetats</string>
<string name="hold_to_append">Manteniu premut per afegir a la cua</string>
@@ -429,10 +429,10 @@
<string name="toast_no_player">No s\'ha trobat cap aplicació que pugui reproduir aquest fitxer</string>
<string name="clear_views_history_title">Esborra l\'historial de visualitzacions</string>
<string name="clear_views_history_title">Esborra l\'historial de reproduccions</string>
<string name="clear_views_history_summary">Esborra l\'historial dels vídeos que s\'han reproduït.</string>
<string name="delete_view_history_alert">Esborra tot l\'historial de visualitzacions.</string>
<string name="view_history_deleted">S\'ha esborrat l\'historial de visualitzacions.</string>
<string name="delete_view_history_alert">Esborra tot l\'historial de reproduccions.</string>
<string name="view_history_deleted">S\'ha esborrat l\'historial de reproduccions.</string>
<string name="clear_search_history_title">Esborra l\'historial de cerca</string>
<string name="clear_search_history_summary">Esborra l\'historial de paraules cercades.</string>
<string name="delete_search_history_alert">Esborra tot l\'historial de cerca.</string>
@@ -440,4 +440,30 @@
<string name="one_item_deleted">S\'ha esborrat 1 element.</string>
<string name="app_license">NewPipe és programari lliure sota llicència copyleft: podeu fer-lo servir, estudiar-lo, compartir-lo i millorar-lo al vostre gust. En concret, podeu redistribuir-lo i/o modificar-lo d\'acord amb els termes de la llicència GNU GPL publicada per la Free Software Foundation, ja sigui la versió 3 o (segons vulgueu) qualsevol altra versió posterior.</string>
<string name="import_settings">Voleu importar també la configuració?</string>
<string name="privacy_policy_title">Política de privacitat del NewPipe</string>
<string name="privacy_policy_encouragement">El projecte NewPipe es pren molt seriosament la vostra privacitat. Per aquesta raó, l\'aplicació no emmagatzema cap mena de dades sense el vostre consentiment.
\nLa política de privacitat del NewPipe descriu detalladament quines dades s\'envien i s\'emmagatzemen quan envieu un informe d\'error.</string>
<string name="read_privacy_policy">Llegeix la política de privacitat</string>
<string name="start_accept_privacy_policy">Per tal de complir amb el Reglament General de Protecció de Dades europeu (GDPR), us demanem que poseu atenció a la política de privacitat del NewPipe. Llegiu-la detingudament.
\nSi voleu enviar-nos un informe d\'error, l\'haureu d\'acceptar.</string>
<string name="accept">Accepta</string>
<string name="decline">Rebutja</string>
<string name="limit_data_usage_none_description">Sense restriccions</string>
<string name="limit_mobile_data_usage_title">Restringeix la resolució quan es facin servir dades mòbils</string>
<string name="minimize_on_exit_title">Minimitza en canviar d\'aplicació</string>
<string name="minimize_on_exit_summary">Acció en canviar a una altra aplicació des del reproductor de vídeo principal — %s</string>
<string name="minimize_on_exit_none_description">Cap</string>
<string name="minimize_on_exit_background_description">Minimitza al reproductor en segon pla</string>
<string name="minimize_on_exit_popup_description">Minimitza al reproductor emergent</string>
<string name="skip_silence_checkbox">Avança ràpid durant el silenci</string>
<string name="playback_step">Pas</string>
<string name="playback_reset">Reinicialitza</string>
<string name="channels">Canals</string>
<string name="playlists">Llistes de reproducció</string>
<string name="tracks">Pistes</string>
<string name="users">Usuaris</string>
</resources>

View File

@@ -1,2 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">点按搜索即可开始</string>
<string name="view_count_text">%1$s 意见</string>
<string name="upload_date_text">发布 %1$s</string>
<string name="no_player_found">找不到流播放器。你想安装VLC吗</string>
<string name="no_player_found_toast">找不到流播放器您可以安装VLC播放它</string>
<string name="install">安装</string>
<string name="cancel">取消</string>
<string name="open_in_browser">在浏览器中打开</string>
<string name="open_in_popup_mode">在弹出模式下打开</string>
<string name="share">分享</string>
<string name="download">下载</string>
<string name="controls_download_desc">下载流文件.</string>
<string name="search">搜索</string>
<string name="settings">设置</string>
<string name="did_you_mean">你的意思是: %1$s ?</string>
<string name="share_dialog_title">与某人分享</string>
<string name="choose_browser">选择浏览器</string>
<string name="screen_rotation">回转</string>
<string name="use_external_video_player_title">使用外部视频播放器</string>
<string name="use_external_video_player_summary">启用此选项时,某些分辨率将不会有音频</string>
<string name="use_external_audio_player_title">使用外部音频播放器</string>
<string name="popup_mode_share_menu_title">NewPipe弹出模式</string>
<string name="subscribe_button_title">订阅</string>
<string name="subscribed_button_title">订阅</string>
<string name="channel_unsubscribed">取消订阅成功</string>
<string name="subscription_change_failed">无法更改订阅</string>
<string name="subscription_update_failed">无法更新订阅</string>
<string name="show_info">显示详情</string>
<string name="tab_main">主页</string>
<string name="tab_subscriptions">订阅</string>
<string name="tab_bookmarks">书签</string>
<string name="fragment_whats_new">新功能</string>
<string name="controls_background_title">后台</string>
<string name="controls_popup_title">弹出窗口</string>
<string name="controls_add_to_playlist_title">添加到</string>
<string name="download_path_title">视频下载路径</string>
<string name="download_path_summary">储存视频文件的路径</string>
<string name="download_path_dialog_title">输入视频的下载地址</string>
<string name="download_path_audio_title">音频下载路径</string>
<string name="download_path_audio_summary">储存音频的路径</string>
<string name="download_path_audio_dialog_title">输入音频的下载路径</string>
<string name="autoplay_by_calling_app_title">自动播放</string>
<string name="autoplay_by_calling_app_summary">NewPipes被其它程序调用时播放视频</string>
<string name="default_resolution_title">默认分辨率</string>
<string name="default_popup_resolution_title">默认弹出窗口分辨率</string>
<string name="show_higher_resolutions_title">显示更高的分辨率</string>
<string name="show_higher_resolutions_summary">只有部分设备支持播放 2K/4K 视频</string>
<string name="play_with_kodi_title">用 Kodi 播放</string>
<string name="kore_not_found">没找到 Kore 应用,需要安装它吗?</string>
<string name="show_play_with_kodi_title">显示“用 Kodi 播放”选项</string>
<string name="show_play_with_kodi_summary">显示用 Kodi媒体中心 播放视频的选项</string>
<string name="play_audio">音频</string>
<string name="default_audio_format_title">默认音频格式</string>
<string name="default_video_format_title">默认视频格式</string>
<string name="theme_title">主题</string>
<string name="light_theme_title">亮色</string>
<string name="dark_theme_title">酷黑</string>
<string name="black_theme_title">黑色</string>
<string name="popup_remember_size_pos_title">记住弹出窗口的尺寸与位置</string>
<string name="m4a_description">M4A — 更好的音质</string>
<string name="popup_remember_size_pos_summary">记住上一次弹出窗口的位置以及大小</string>
<string name="thumbnail_cache_wipe_complete_notice">清理照片内存</string>
<string name="minimize_on_exit_popup_description">最小化弹出播放器</string>
</resources>

View File

@@ -32,7 +32,7 @@
<string name="show_play_with_kodi_summary">Zobrazit možnost přehrání videa pomocí multimediálního centra Kodi</string>
<string name="play_audio">Zvuk</string>
<string name="default_audio_format_title">Výchozí formát zvuku</string>
<string name="webm_description">WebM — svobodný formát</string>
<string name="webm_description">WebM — volný formát</string>
<string name="m4a_description">M4A — lepší kvalita</string>
<string name="theme_title">Téma</string>
<string name="dark_theme_title">Tmavé</string>
@@ -40,7 +40,7 @@
<string name="download_dialog_title">Stáhnout</string>
<string name="next_video_title">Další videa</string>
<string name="show_next_and_similar_title">Zobrazovat další a podobná videa</string>
<string name="show_next_and_similar_title">Zobrazovat \'další\' a \'podobná\' videa</string>
<string name="url_not_supported_toast">URL není podporováno</string>
<string name="search_language_title">Preferovaný jazyk obsahu</string>
<string name="settings_category_video_audio_title">Video a zvuk</string>
@@ -67,7 +67,7 @@
<string name="err_dir_create">Nebylo možné vytvořit složku pro stažené soubory \'%1$s\'</string>
<string name="info_dir_created">Vytvořena složka pro stažené soubory \'%1$s\'</string>
<string name="autoplay_by_calling_app_title">Automaticky přehrávat</string>
<string name="autoplay_by_calling_app_summary">Automaticky přehrát video, když je NewPipe otevřen z jiné aplikace</string>
<string name="autoplay_by_calling_app_summary">Přehrá video, když je NewPipe otevřen z jiné aplikace</string>
<string name="content">Obsah</string>
<string name="show_age_restricted_content_title">Zobrazovat věkově omezený obsah</string>
<string name="video_is_age_restricted">Toto video je věkově omezeno. Povolte věkově omezená videa v nastavení.</string>
@@ -172,7 +172,7 @@ otevření ve vyskakovacím okně</string>
<string name="show_search_suggestions_summary">Zobrazovat návrhy při vyhledávání</string>
<string name="enable_search_history_title">Historie vyhledávání</string>
<string name="enable_search_history_summary">Hledané výrazy lokálně uchovávat</string>
<string name="enable_watch_history_title">Historie sledování</string>
<string name="enable_watch_history_title">Historie &amp; mezipaměť</string>
<string name="enable_watch_history_summary">Evidovat zhlédnutá videa</string>
<string name="resume_on_audio_focus_gain_title">Přehrávat po přechodu do popředí</string>
<string name="resume_on_audio_focus_gain_summary">Pokračovat v přehrávání po přerušení (např. hovor)</string>
@@ -239,7 +239,7 @@ otevření ve vyskakovacím okně</string>
<string name="tab_about">O aplikaci</string>
<string name="tab_contributors">Přispěvatelé</string>
<string name="tab_licenses">Licence</string>
<string name="app_description">Bezplatná a nenáročná YouTube aplikace pro Android.</string>
<string name="app_description">Svobodné a nenáročné streamování v Androidu.</string>
<string name="view_on_github">Zobraz na GitHubu</string>
<string name="app_license_title">Licence NewPipe</string>
<string name="contribution_encouragement">Pokud máte nápady na zlepšení jako; překlad, změny designu, vylepšování kódu nebo opravdu velké změny kódu - pomoc je vždy vítána. Čím více se udělá, tím lepší to bude!</string>
@@ -254,13 +254,13 @@ otevření ve vyskakovacím okně</string>
<string name="history_empty">Historie je prázdná</string>
<string name="history_cleared">Historie vymazána</string>
<string name="item_deleted">Položka byla odstraněna</string>
<string name="show_hold_to_append_title">Zobrazovat tip \"Podrž pro přidání\"</string>
<string name="show_hold_to_append_title">Zobrazovat tip \"podržet pro přidání\"</string>
<string name="show_hold_to_append_summary">Zobrazí se po stisku tlačítek přehrát na pozadí nebo přehrát v okně na stránce s videem</string>
<string name="background_player_append">Ve frontě přehrávače na pozadí</string>
<string name="popup_playing_append">Ve frontě přehrávače v okně</string>
<string name="play_all">Přehrát vše</string>
<string name="player_stream_failure">Přehrávání streamu selhalo</string>
<string name="player_stream_failure">Tento stream nelze přehrát</string>
<string name="player_unrecoverable_failure">Došlo k neobnovitelné chybě přehrávače</string>
<string name="player_recoverable_failure">Obnovování z chyby přehrávače</string>
@@ -294,10 +294,10 @@ otevření ve vyskakovacím okně</string>
<string name="start_here_on_background">Začne zde na pozadí</string>
<string name="start_here_on_popup">Začne zde v okně</string>
<string name="donation_title">Donate</string>
<string name="donation_encouragement">NewPipe je vyvíjen dobrovolníky, kteří tráví svůj volný čas, aby vaše zkušenost s aplikací byla co nejlepší. Nyní je čas vrátit něco zpět, aby naši vývojáři mohli NewPipe dále zlepšovat a zároveň si vychutnat šálek kávy!</string>
<string name="donation_encouragement">NewPipe je vyvíjen dobrovolníky, kteří tráví svůj čas, aby vaše zkušenost s aplikací byla co nejlepší. Vraťte vývojářům něco zpět, aby mohli NewPipe dále zlepšovat a zároveň si vychutnat šálek kávy.</string>
<string name="give_back">Daruj</string>
<string name="website_title">Webová stránka</string>
<string name="website_encouragement">Pro další informace a poslední novinky o NewPipe navštivte naši stránku.</string>
<string name="website_encouragement">Pro další informace a novinky navštivte webovou stránku NewPipe.</string>
<string name="default_content_country_title">Země výchozího obsahu</string>
<string name="service_title">Služba</string>
<string name="toggle_orientation">Změna orientaci</string>
@@ -332,7 +332,7 @@ otevření ve vyskakovacím okně</string>
<string name="always_ask_player">Vždy se ptát</string>
<string name="preferred_player_fetcher_notification_title">Získávám informace…</string>
<string name="preferred_player_fetcher_notification_message">Požadovaný obsah se načítá</string>
<string name="preferred_player_fetcher_notification_message">Načítání požadovaného obsahu</string>
<string name="controls_download_desc">Stáhnout soubor streamu.</string>
<string name="show_info">Ukázat informace</string>
@@ -353,7 +353,7 @@ otevření ve vyskakovacím okně</string>
<string name="title_last_played">Poslední přehráno</string>
<string name="title_most_played">Nejvíce přehráno</string>
<string name="drawer_header_action_paceholder_text">Zde něco brzy bude ;D</string>
<string name="drawer_header_action_paceholder_text">Zde se brzy něco objeví ;D</string>
<string name="always_ask_open_action">Vždy se zeptat</string>
@@ -369,16 +369,16 @@ otevření ve vyskakovacím okně</string>
<string name="unbookmark_playlist">Smazat záložku</string>
<string name="delete_playlist_prompt">Přejete si smazat tento playlist?</string>
<string name="playlist_creation_success">Playlist úspěšně vytvořen</string>
<string name="playlist_creation_success">Playlist vytvořen</string>
<string name="playlist_add_stream_success">Přidáno do playlistu</string>
<string name="playlist_thumbnail_change_success">Náhled playlistu změněn</string>
<string name="playlist_delete_failure">Nezdařilo se smazat playlist</string>
<string name="playlist_delete_failure">Playlist nelze smazat</string>
<string name="caption_none">Bez poznámek</string>
<string name="caption_none">Žádné poznámky</string>
<string name="resize_fit">PŘIZPŮSOBIT</string>
<string name="resize_fill">VYPLNIT</string>
<string name="resize_zoom">ZVĚTŠIT</string>
<string name="resize_fit">Přizpůsobit</string>
<string name="resize_fill">Vyplnit</string>
<string name="resize_zoom">Zvětšit</string>
<string name="caption_font_size_settings_title">Velikost písma nadpisu</string>
<string name="smaller_caption_font_size">Menší písmo</string>
@@ -391,10 +391,10 @@ otevření ve vyskakovacím okně</string>
<string name="settings_category_debug_title">Ladění</string>
<string name="caption_auto_generated">"Automaticky generováno "</string>
<string name="enable_leak_canary_title">Povolit službu LeakCanary</string>
<string name="enable_leak_canary_summary">Monitoring úniku paměti může vést k nereagování aplikace při zátěži</string>
<string name="enable_leak_canary_summary">Monitoring úniku paměti může způsobit nereagování aplikace při heap dumpingu</string>
<string name="enable_disposed_exceptions_title">Nahlásit mimocyklické chyby</string>
<string name="enable_disposed_exceptions_summary">Vynutit vykazování výjimek Rx, které se vyskytnou mimo fragment nebo životnost cyklu po odstranění</string>
<string name="enable_disposed_exceptions_title">Nahlásit mimo-cyklické chyby</string>
<string name="enable_disposed_exceptions_summary">Vynutit vykazování výjimek Rx mimo fragment nebo životnost cyklu po odstranění</string>
<string name="use_inexact_seek_title">Použít rychlé nepřesné hledání</string>
<string name="use_inexact_seek_summary">Nepřesné hledání umožní přehrávači posouvat se rychleji, ale se sníženou přesností</string>
@@ -404,8 +404,8 @@ otevření ve vyskakovacím okně</string>
<string name="metadata_cache_wipe_title">Vymazat metadata v mezipaměti</string>
<string name="metadata_cache_wipe_summary">Odebrat všechna data uložená v mezipaměti</string>
<string name="metadata_cache_wipe_complete_notice">Mezipaměť metadat vymazána</string>
<string name="auto_queue_title">Auto-fronta dalšího streamu</string>
<string name="auto_queue_summary">Automaticky připojí související stream, když přehřávání začne na posledním streamu v neopakující se přehráváné frontě.</string>
<string name="auto_queue_title">Automatická fronta dalšího streamu</string>
<string name="auto_queue_summary">Automaticky připojí související stream při přehrávání posledního streamu v neopakující se frontě.</string>
<string name="file">Soubor</string>
<string name="invalid_directory">Neplatný adresář</string>
@@ -428,26 +428,24 @@ otevření ve vyskakovacím okně</string>
<string name="import_file_title">Import souboru</string>
<string name="previous_export">Předchozí export</string>
<string name="subscriptions_import_unsuccessful">Import odběrů selhal</string>
<string name="subscriptions_export_unsuccessful">Export odběrů selhal</string>
<string name="subscriptions_import_unsuccessful">Odběry nelze importovat</string>
<string name="subscriptions_export_unsuccessful">Odběry nelze exportovat</string>
<string name="import_youtube_instructions">K importu vašich YouTube odběrů potřebujete exportní soubor, který lze stáhnout následujícím způsobem:
\n
\n1. Přejděte na tuto adresu: %1$s
\n2. Na vyžádání se přihlašte ke svému účtu
<string name="import_youtube_instructions">Importovat YouTube odběry stáhnutím exportního souboru:
\n
\n1. Přejděte na tuto URL adresu: %1$s
\n2. Na vyžádání se přihlašte
\n3. Mělo by začít stahování (onoho exportního souboru)</string>
<string name="import_soundcloud_instructions">K importu vašich SoundCloud sledovaných musíte znát URL adresu nebo ID vašeho profilu. Pokud jednu z těchto věcí znáte, zadejte ji níže a to je celé.
\n
\nPokud ne, můžete postupovat dle následujících kroků:
\n
\n1. Ve svém prohlížeči povolte \"režim pro PC\" (pro mobilní zařízení není stránka dostupná)
\n2. Přejděte na tuto adresu: %1$s
\n3. Na vyžádání se přihlašte ke svému účtu
\n4. Zkopírujte URL adresu, na kterou jste byli přesměrováni (to je URL adresa vašeho profilu)</string>
<string name="import_soundcloud_instructions_hint">tvojeid, soundcloud.com/tvojeid</string>
<string name="import_soundcloud_instructions">Importovat SoundCloud profil zadáním URL adresy nebo vašeho ID:
\n
\n1. Ve svém prohlížeči povolte \"režim pro PC\" (pro mobilní zařízení není stránka dostupná)
\n2. Přejděte na tuto URL adresu: %1$s
\n3. Na vyžádání se přihlašte
\n4. Zkopírujte URL adresu profilu, na kterou jste byli přesměrováni.</string>
<string name="import_soundcloud_instructions_hint">vašeID, soundcloud.com/yourid</string>
<string name="import_network_expensive_warning">Mějte na paměti, že tato operace může být náročná na data.
\n
<string name="import_network_expensive_warning">Pamatujte, že tato operace může být náročná na data.
\n
\nChcete pokračovat?</string>
<string name="playback_speed_control">Ovládání rychlosti přehrávání</string>
@@ -458,13 +456,13 @@ otevření ve vyskakovacím okně</string>
<string name="playback_default">Výchozí nastavení</string>
<string name="no_streams_available_download">Ke stažení nejsou dostupné žádné streamy</string>
<string name="preferred_open_action_settings_title">Preferovaná akce při otevření</string>
<string name="preferred_open_action_settings_title">Preferovaná \'otevřít\' akce</string>
<string name="preferred_open_action_settings_summary">Výchozí chování při otevírání obsahu — %s</string>
<string name="caption_setting_title">Nastavení poznámek</string>
<string name="caption_setting_description">Upravuje velikost textu poznámek a styly pozadí. Změny se projeví po restartu přehrávače.</string>
<string name="caption_setting_title">Poznámky</string>
<string name="caption_setting_description">Upravuje velikost textu poznámek a styly pozadí. Změny se projeví po restartu aplikace.</string>
<string name="toast_no_player">Pro tento soubor nebyl nalezen žádný přehrávač</string>
<string name="toast_no_player">K přehrání tohoto souboru chybí vhodná aplikace</string>
<string name="clear_views_history_title">Vymazat historii sledování</string>
<string name="clear_views_history_summary">Vymaže historii přehrávaných streamů.</string>
@@ -476,4 +474,5 @@ otevření ve vyskakovacím okně</string>
<string name="search_history_deleted">Historie vyhledávání smazána.</string>
<string name="one_item_deleted">Jedna položka smazána.</string>
<string name="app_license">NewPipe je copyleft libre software: Můžete jej používat, sdílet a vylepšovat dle vaší vůle. Redistribuovat a/nebo upravovat lze za podmínek GNU General Public Licence zveřejňované nadací Free Software Foundation, a to buď za podmínek licence verze 3 nebo (dle vaší volby) jakékoli pozdější verze.</string>
</resources>

View File

@@ -12,14 +12,14 @@
<string name="settings">Einstellungen</string>
<string name="did_you_mean">Meintest du: %1$s ?</string>
<string name="share_dialog_title">Teilen mit</string>
<string name="choose_browser">Browser</string>
<string name="choose_browser">Browser auswählen</string>
<string name="screen_rotation">Drehen des Geräts</string>
<string name="download_path_title">Download-Verzeichnis für Videos</string>
<string name="download_path_summary">Verzeichnis für heruntergeladene Videos</string>
<string name="download_path_dialog_title">Download-Verzeichnis für Videos angeben</string>
<string name="default_resolution_title">Standardauflösung</string>
<string name="play_with_kodi_title">Mit Kodi abspielen</string>
<string name="kore_not_found">Kore App wurde nicht gefunden. Möchten Sie Kore jetzt installieren?</string>
<string name="kore_not_found">Kore App wurde nicht gefunden. Kore jetzt installieren?</string>
<string name="show_play_with_kodi_title">Zeige \"Mit Kodi abspielen\" Option</string>
<string name="show_play_with_kodi_summary">Zeigt eine Option an, über die man Videos mit Kodi abspielen kann</string>
<string name="play_audio">Audio</string>
@@ -28,7 +28,7 @@
<string name="m4a_description">M4A — bessere Qualität</string>
<string name="download_dialog_title">Herunterladen</string>
<string name="next_video_title">Nächstes Video</string>
<string name="show_next_and_similar_title">Zeige nächstes und ähnliche Videos</string>
<string name="show_next_and_similar_title">\'Nächste\' und \'ähnliche\' Videos anzeigen</string>
<string name="url_not_supported_toast">URL wird nicht unterstützt</string>
<string name="settings_category_video_audio_title">Video &amp; Audio</string>
<string name="search_language_title">Bevorzugte Sprache des Inhalts</string>
@@ -67,7 +67,7 @@
<string name="content">Inhalt</string>
<string name="show_age_restricted_content_title">Altersbeschränkte Inhalte anzeigen</string>
<string name="video_is_age_restricted">Video ist altersbeschränkt. Schalten Sie erst altersbeschränkte Videos in den Einstellungen ein.</string>
<string name="video_is_age_restricted">Video ist altersbeschränkt. Schalte erst altersbeschränkte Videos in den Einstellungen ein.</string>
<string name="could_not_setup_download_menu">Konnte Download-Menü nicht einrichten</string>
<string name="live_streams_not_supported">Dies ist ein LIVESTREAM. Diese werden noch nicht unterstützt.</string>
@@ -90,13 +90,13 @@
<string name="your_comment">Dein Kommentar (auf englisch):</string>
<string name="could_not_get_stream">Konnte keinen Stream holen</string>
<string name="autoplay_by_calling_app_title">Autoplay</string>
<string name="autoplay_by_calling_app_summary">Spiele ein Video automatisch ab, wenn NewPipe von einer anderen App aufgerufen wurde</string>
<string name="autoplay_by_calling_app_summary">Spiele ein Video ab, wenn NewPipe von einer anderen App aufgerufen wurde</string>
<string name="report_error">Einen Fehler melden</string>
<string name="user_report">Anwenderbericht</string>
<string name="duration_live">Live</string>
<string name="main_bg_subtitle">„Suchen“ antippen um zu beginnen</string>
<string name="main_bg_subtitle">„Suchen“ antippen, um zu beginnen</string>
<string name="downloads">Downloads</string>
<string name="downloads_title">Downloads</string>
<string name="error_report_title">Fehlerbericht</string>
@@ -115,7 +115,7 @@
<string name="msg_exists">Datei existiert bereits</string>
<string name="msg_wait">Bitte warten…</string>
<string name="msg_copied">In Zwischenablage kopiert</string>
<string name="no_available_dir">Bitte wählen Sie ein verfügbares Download-Verzeichnis</string>
<string name="no_available_dir">Bitte wähle ein verfügbares Download-Verzeichnis</string>
<string name="start">Starten</string>
<string name="pause">Pause</string>
@@ -134,7 +134,7 @@
<string name="black_theme_title">Schwarz</string>
<string name="reCaptcha_title">reCAPTCHA-Aufgabe</string>
<string name="recaptcha_request_toast">reCAPTCHA-Herausforderung angefordert</string>
<string name="recaptcha_request_toast">reCAPTCHA-Aufgabe angefordert</string>
<string name="later">Später</string>
@@ -185,14 +185,14 @@
<string name="error_unable_to_load_license">Lizenz konnte nicht geladen werden</string>
<string name="copyright" formatted="true">© %1$s von %2$s unter %3$s</string>
<string name="tab_about">Über</string>
<string name="app_description">Eine offene, schlanke YouTube App für Android.</string>
<string name="app_description">Eine freie, schlanke Streaming-App für Android.</string>
<string name="app_license_title">NewPipes Lizenz</string>
<string name="contribution_encouragement">Ob Ideen, Übersetzung, Design-Änderungen, Code-Aufräumung oder richtig große Code-Änderungen - Hilfe ist immer willkommen. Je mehr geholfen wird, desto besser wird NewPipe!</string>
<string name="contribution_encouragement">Ob Ideen, Übersetzungen, Design-Änderungen, Code-Aufräumung oder richtig große Code-Änderungen Hilfe ist immer willkommen. Je mehr geholfen wird, desto besser wird NewPipe!</string>
<string name="title_licenses">Drittanbieter-Lizenzen</string>
<string name="view_on_github">Auf GitHub ansehen</string>
<string name="contribution_title">Beitragen</string>
<string name="settings_category_downloads_title">Download</string>
<string name="settings_file_charset_title">Erlaubt Zeichen im Dateinamen</string>
<string name="settings_file_charset_title">Erlaubte Zeichen im Dateinamen</string>
<string name="settings_file_replacement_character_summary">Ungültige Zeichen werden durch dieses Zeichen ersetzt</string>
<string name="settings_file_replacement_character_title">Ersetzungszeichen</string>
@@ -222,7 +222,7 @@
<string name="resume_on_audio_focus_gain_summary">Nach Unterbrechungen (z.B. Telefonaten) Wiedergabe fortsetzen</string>
<string name="notification_channel_name">NewPipe Benachrichtigung</string>
<string name="notification_channel_name">NewPipe-Benachrichtigung</string>
<string name="notification_channel_description">Benachrichtigungen für NewPipe-Hintergrund- und Pop-up Player</string>
<string name="tab_main">Hauptmenü</string>
@@ -255,7 +255,7 @@
<string name="item_deleted">Element gelöscht</string>
<string name="resume_on_audio_focus_gain_title">Fortsetzen bei erneutem Fokussieren</string>
<string name="settings_category_player_title">Player</string>
<string name="empty_subscription_feed_subtitle">Nichts hier außer Grillen</string>
<string name="empty_subscription_feed_subtitle">Nichts hier außer das Zirpen der Grillen</string>
<string name="delete_item_search_history">Möchtest du dieses Element aus dem Suchverlauf löschen?</string>
<string name="blank_page_summary">Leere Seite</string>
@@ -266,8 +266,8 @@
<string name="play_all">Alles abspielen</string>
<string name="play_queue_remove">Entfernen</string>
<string name="play_queue_audio_settings">Audio Einstellungen</string>
<string name="player_stream_failure">Abspielen des Streams fehlgeschlagen</string>
<string name="play_queue_audio_settings">Audio-Einstellungen</string>
<string name="player_stream_failure">Konnte diesen Stream nicht abspielen</string>
<string name="main_page_content">Inhalt der Hauptseite</string>
<string name="subscription_page_summary">Abonnement-Seite</string>
<string name="feed_page_summary">Feed-Seite</string>
@@ -286,7 +286,7 @@
<string name="show_hold_to_append_summary">Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Button auf der Videodetailseite gedrückt gehalten wird</string>
<string name="background_player_append">In der Warteschlange der Hintergrundwiedergabe</string>
<string name="new_and_hot">Neu &amp; Heiß</string>
<string name="hold_to_append">Zum Anfügen gedrückt halten</string>
<string name="hold_to_append">Halten, um zur Wiedergabeliste hinzuzufügen</string>
<string name="show_hold_to_append_title">\"Gedrückt halten, um Tipp hinzuzufügen\" anzeigen</string>
<string name="unknown_content">[Unbekannt]</string>
@@ -298,10 +298,10 @@
<string name="donation_title">Spenden</string>
<string name="give_back">Zurückgeben</string>
<string name="website_title">Website</string>
<string name="website_encouragement">Um mehr Informationen und die neuesten Nachrichten über NewPipe zu erhalten, besuche unsere Website.</string>
<string name="donation_encouragement">NewPipe wird von Freiwilligen entwickelt, die ihre Freizeit dafür aufbringen, die beste Nutzererfahrung zu bieten. Jetzt ist es an Dir, ein wenig zurückzugeben und sicherzustellen, dass unsere Entwickler NewPipe noch besser machen können, während sie sich an einer Tasse Java-Kaffee erfreuen!</string>
<string name="website_encouragement">Besuche die NewPipe-Website für weitere Informationen und Neuigkeiten.</string>
<string name="donation_encouragement">NewPipe wird von Freiwilligen entwickelt, die ihre Freizeit dafür verwenden, dir die beste Nutzererfahrung zu bieten. Gib etwas zurück, um den Entwicklern zu helfen, NewPipe noch besser machen zu können, während sie sich an einer Tasse Kaffee erfreuen.</string>
<string name="service_title">Service</string>
<string name="no_player_found_toast">Kein Streamplayer gefunden (Du kannst VLC installieren, um den Stream abzuspielen)</string>
<string name="no_player_found_toast">Keinen Streamplayer gefunden (du kannst VLC installieren, um den Stream abzuspielen)</string>
<string name="default_content_country_title">Standard-Land des Inhalts</string>
<string name="always">Immer</string>
<string name="just_once">Nur einmal</string>
@@ -325,7 +325,7 @@
<string name="always_ask_player">Immer fragen</string>
<string name="preferred_player_fetcher_notification_title">Informationen werden abgerufen…</string>
<string name="preferred_player_fetcher_notification_message">Der angeforderte Inhalt wird geladen</string>
<string name="preferred_player_fetcher_notification_message">Gewünschten Inhalt laden</string>
<string name="import_data_title">Datenbank importieren</string>
<string name="export_data_title">Datenbank exportieren</string>
<string name="import_data_summary">Wird deinen Verlauf und deine Abos überschreiben</string>
@@ -358,7 +358,7 @@
<string name="delete_playlist">Wiedergabeliste löschen</string>
<string name="rename_playlist">Wiedergabeliste umbenennen</string>
<string name="append_playlist">Zur Wiedergabeliste hinzufügen</string>
<string name="set_as_playlist_thumbnail">Als Vorschaubild der Wiedergabeliste festlegen</string>
<string name="set_as_playlist_thumbnail">Als Symbolbild der Wiedergabeliste festlegen</string>
<string name="unbookmark_playlist">Lesezeichen entfernen</string>
@@ -373,8 +373,8 @@
<string name="caption_font_size_settings_title">Schriftgröße der Untertitel</string>
<string name="dismiss">Abbrechen</string>
<string name="normal_caption_font_size">Normale Schriftgröße</string>
<string name="controls_download_desc">Stream-Datei herunterladen</string>
<string name="use_inexact_seek_title">Benutze schnelle ungenaue Suche</string>
<string name="controls_download_desc">Stream-Datei herunterladen.</string>
<string name="use_inexact_seek_title">Benutze schnelle, ungenaue Suche</string>
<string name="use_inexact_seek_summary">Mit ungenauem Suchen kann die Abspielposition schneller erreicht werden, aber auf Kosten der Genauigkeit</string>
<string name="file">Datei</string>
@@ -403,64 +403,62 @@
<string name="download_thumbnail_title">Vorschaubilder laden</string>
<string name="thumbnail_cache_wipe_complete_notice">Bildercache gelöscht</string>
<string name="metadata_cache_wipe_title">Leere die gecachten Metadaten</string>
<string name="metadata_cache_wipe_summary">Entfene alle gecachten Website-Daten</string>
<string name="metadata_cache_wipe_summary">Entferne alle gecachten Website-Daten</string>
<string name="metadata_cache_wipe_complete_notice">Metadatencache gelöscht</string>
<string name="settings_category_debug_title">Debug</string>
<string name="invalid_source">Ungültige Datei-/Inhaltsquelle</string>
<string name="export_complete_toast">Export fertiggestellt</string>
<string name="import_complete_toast">Import fertiggestellt</string>
<string name="export_complete_toast">Export abgeschlossen</string>
<string name="import_complete_toast">Import abgeschlossen</string>
<string name="playlist_name_input">Name</string>
<string name="import_export_title">Import/Export</string>
<string name="import_title">Import</string>
<string name="subscriptions_import_unsuccessful">Import der Abonnements fehlgeschlagen</string>
<string name="subscriptions_export_unsuccessful">Export der Abonnements fehlgeschlagen</string>
<string name="subscriptions_import_unsuccessful">Abonnements konnten nicht importiert werden</string>
<string name="subscriptions_export_unsuccessful">Abonnements konnten nicht exportiert werden</string>
<string name="playback_speed_control">Wiedergabegeschwindigkeit</string>
<string name="playback_speed_control">Wiedergabegeschwindigkeitsregler</string>
<string name="playback_tempo">Geschwindigkeit</string>
<string name="playback_pitch">Tonhöhe</string>
<string name="unhook_checkbox">Aushaken (kann zu Verzerrungen führen)</string>
<string name="unhook_checkbox">Verknüpfung aufheben (kann zu Verzerrungen führen)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Standard</string>
<string name="download_thumbnail_summary">Deaktivieren Sie diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn Sie dies ändern, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht.</string>
<string name="download_thumbnail_summary">Deaktiviere diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn du dies änderst, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht.</string>
<string name="auto_queue_title">Nächsten Stream automatisch einreihen</string>
<string name="auto_queue_summary">Fügen Sie automatisch einen zugehörigen Stream hinzu, wenn die Wiedergabe auf dem letzten Stream in einer sich nicht wiederholenden Wiedergabewarteschlange beginnt.</string>
<string name="auto_queue_summary">Automatisches Anhängen eines verwandten Streams beim Abspielen des letzten Streams in einer nicht wiederholten Warteschlange.</string>
<string name="drawer_header_action_paceholder_text">Hier wird bald etwas stehen ;D</string>
<string name="bookmark_playlist">Lesezeichen Wiedergabeliste</string>
<string name="resize_fit">ANPASSEN</string>
<string name="resize_fill">FÜLLEN</string>
<string name="resize_zoom">VERGRÖßERN</string>
<string name="resize_fit">Anpassen</string>
<string name="resize_fill">Füllen</string>
<string name="resize_zoom">Vergrößern</string>
<string name="enable_leak_canary_summary">Speicherlecküberwachung kann dazu führen, dass die App beim Heap-Dumping nicht mehr reagiert</string>
<string name="enable_disposed_exceptions_title">Fehler außerhalb des Lebenszyklus melden</string>
<string name="enable_disposed_exceptions_summary">Erzwingen der Meldung unzustellbarer Rx-Ausnahmen, die außerhalb des Lebenszyklus von Fragmenten oder Aktivitäten nach der Entsorgung auftreten</string>
<string name="enable_disposed_exceptions_summary">Erzwingen der Meldung unzustellbarer Rx-Ausnahmen außerhalb des Lebenszyklus von Fragmenten oder Aktivitäten nach der Entsorgung</string>
<string name="import_youtube_instructions">Um Ihre YouTube-Abonnements zu importieren, benötigen Sie die Exportdatei, die Sie mit dieser Anleitung herunterladen können:
\n
\n1. Gehen Sie zu dieser URL: %1$s
\n2. Melden Sie sich bei Ihrem Konto an, wenn Sie dazu aufgefordert werden.
<string name="import_youtube_instructions">Importieren Sie YouTube-Abonnements, indem Sie die Exportdatei herunterladen:
\n
\n1. Gehen Sie zu dieser URL: %1$s
\n2. Melden Sie sich an, wenn Sie dazu aufgefordert werden.
\n3. Der Ladevorgang sollte beginnen (das ist die Exportdatei)</string>
<string name="import_soundcloud_instructions">Um Ihre SoundCloud-Folgen zu importieren, müssen Sie Ihre Profil-URL oder -ID kennen. Wenn ja, geben Sie einfach einen der beiden in die untenstehende Eingabe ein und Sie können loslegen.
<string name="import_soundcloud_instructions">Importieren Sie ein SoundCloud-Profil, indem Sie entweder die URL oder Ihre ID eingeben:
\n
\nWenn nicht, können Sie diesen Schritten folgen:
\n
\n1. Aktivieren Sie den \"Desktop-Modus\" in einigen Browsern (die Seite ist für mobile Geräte nicht verfügbar).
\n1. Aktivieren des \"Desktop-Modus\" in einem Web-Browser (die Seite ist für mobile Geräte nicht verfügbar)
\n2. Gehen Sie zu dieser URL: %1$s
\n3. Melden Sie sich bei Ihrem Konto an, wenn Sie dazu aufgefordert werden.
\n4. Kopieren Sie die URL, zu der Sie umgeleitet wurden (das ist Ihre Profil-URL)</string>
<string name="import_soundcloud_instructions_hint">yourid, soundcloud.com/yourid</string>
\n3. Melden Sie sich an, wenn Sie gefragt werden
\n4. Kopieren Sie die Profil-URL, zu der Sie weitergeleitet wurden.</string>
<string name="import_soundcloud_instructions_hint">yourID, soundcloud.com/yourid</string>
<string name="no_streams_available_download">Keine Streams zum Download verfügbar</string>
<string name="preferred_open_action_settings_title">Bevorzugte öffnen Aktion</string>
<string name="preferred_open_action_settings_title">Bevorzugte \'öffnen\' Aktion</string>
<string name="preferred_open_action_settings_summary">Standardaktion beim Öffnen von Inhalten - %s</string>
<string name="caption_setting_title">Untertitel</string>
<string name="caption_setting_description">Ändere Textgröße und Hintergrundstil des Untertitels im Player. Wird erst nach Neustart des App wirksam.</string>
<string name="toast_no_player">Für diese Datei wurde kein Player gefunden</string>
<string name="toast_no_player">Keine App zum Abspielen dieser Datei installiert</string>
<string name="clear_views_history_title">Verlauf leeren</string>
<string name="clear_views_history_summary">Löscht den Verlauf der abgespielten Streams.</string>
@@ -472,4 +470,27 @@
<string name="search_history_deleted">Suchverlauf gelöscht.</string>
<string name="one_item_deleted">1 Element gelöscht.</string>
<string name="app_license">NewPipe ist freie Copyleft-Software: Du kannst es nach Belieben benutzen, studieren, mit anderen teilen und verbessern. Insbesondere kannst du sie unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren, entweder unter Version 3 der Lizenz oder (nach deiner Wahl) jeder späteren Version.</string>
<string name="import_settings">Möchtest du auch Einstellungen importieren?</string>
<string name="privacy_policy_title">NewPipe-Datenschutzbestimmungen</string>
<string name="privacy_policy_encouragement">Dem NewPipe-Projekt ist Datenschutz sehr wichtig. Deshalb sammelt diese App keine Daten ohne deine Zustimmung.
\nNewPipes Datenschutzbestimmungen erklären im Detail, welche Daten beim Absenden eines Absturzberichtes verschickt und gespeichert werden.</string>
<string name="read_privacy_policy">Lies die Datenschutzbestimmungen</string>
<string name="accept">Akzeptieren</string>
<string name="decline">Ablehnen</string>
<string name="start_accept_privacy_policy">Um der europäischen Datenschutz-Grundverordnung (DSGVO) gerecht zu werden, weisen wir hiermit auf NewPipe\'s Datenschutzerklärung hin. Bitte lies sie sorgfältig durch.
\nDu musst den Datenschutzrichtlinien zustimmen, um den Fehlerbericht an uns zu senden.</string>
<string name="limit_data_usage_none_description">Unbegrenzt</string>
<string name="limit_mobile_data_usage_title">Auflösung bei Verwendung mobiler Daten begrenzen</string>
<string name="minimize_on_exit_title">Minimieren beim Anwendungswechsel</string>
<string name="minimize_on_exit_summary">Aktion beim Umschalten auf eine andere Anwendung vom Haupt-Videoplayer - %s</string>
<string name="minimize_on_exit_none_description">Keine</string>
<string name="minimize_on_exit_background_description">Zum Hintergrund-Player minimieren</string>
<string name="minimize_on_exit_popup_description">Zum Popup-Player minimieren</string>
<string name="skip_silence_checkbox">Vorspulen während der Stille</string>
<string name="playback_step">Schritt</string>
<string name="playback_reset">Zurücksetzen</string>
</resources>

View File

@@ -62,4 +62,43 @@
<string name="info_dir_created">Δημιουργήθηκε ο φάκελος \'%1$s\'</string>
<string name="short_billion">Δ</string>
<string name="open_in_popup_mode">Άνοιγμα σε αναδυόμενο παράθυρο</string>
<string name="subscribe_button_title">Εγγραφή</string>
<string name="subscribed_button_title">Εγγεγραμμένος</string>
<string name="show_higher_resolutions_summary">Μόνο μερικές συσκευές υποστηρίζουν την αναπαραγωγή 2K/4K βίντεο</string>
<string name="black_theme_title">Μαύρο</string>
<string name="show_search_suggestions_title">Προτάσεις αναζήτησης</string>
<string name="enable_search_history_title">Ιστορικό αναζήτησης</string>
<string name="downloads">Λήψεις</string>
<string name="downloads_title">Λήψεις</string>
<string name="all">Όλα</string>
<string name="channel">Κανάλι</string>
<string name="yes">Ναι</string>
<string name="later">Αργότερα</string>
<string name="filter">Φίλτρο</string>
<string name="general_error">Σφάλμα</string>
<string name="error_snackbar_action">ΑΝΑΦΟΡΑ</string>
<string name="what_device_headline">Πληροφορίες:</string>
<string name="what_happened_headline">Τι συνέβη:</string>
<string name="your_comment">Το σχόλιό σας (στα Αγγλικά):</string>
<string name="error_details_headline">Λεπτομέρειες:</string>
<string name="report_error">Αναφορά Σφάλματος</string>
<string name="video">Βίντεο</string>
<string name="audio">Ήχος</string>
<string name="pause">Παύση</string>
<string name="delete">Διαγραφή</string>
<string name="msg_error">Σφάλμα</string>
<string name="msg_exists">Το αρχείο υπάρχει ήδη</string>
<string name="msg_running_detail">Πατήστε για λεπτομέρειες</string>
<string name="settings_category_downloads_title">Λήψη</string>
<string name="charset_letters_and_digits">Γράμματα και ψηφία</string>
<string name="action_settings">Ρυθμίσεις</string>
<string name="action_open_website">Άνοιγμα ιστοσελίδας</string>
<string name="tab_licenses">Άδειες</string>
<string name="title_activity_history">Ιστορικό</string>
<string name="action_history">Ιστορικό</string>
<string name="show_info">Εμφάνιση πληροφοριών</string>
</resources>

View File

@@ -45,7 +45,7 @@
<string name="detail_dislikes_img_view_description">Malŝatoj</string>
<string name="use_tor_title">Uzi la programon Tor</string>
<string name="no_player_found">Neniu elsendlflua ludilo trovita. Ĉu instali la aplikaĵon VLC?</string>
<string name="kore_not_found">La aplikaĵo Kore ne estas trovita. Ĉu instali la aplikaĵon Kore?</string>
<string name="kore_not_found">La aplikaĵo Kore ne estas trovita. Ĉu instali ĝin?</string>
<string name="show_next_and_similar_title">Montri la sekvan videon kaj similajn videojn</string>
<string name="could_not_load_thumbnails">Ĉiuj miniaturoj ne ŝargeblas</string>
<string name="youtube_signature_decryption_error">La subskribo de la ligilo de la video ne malĉifreblas.</string>
@@ -59,12 +59,12 @@
<string name="download_path_audio_title">Elŝutujo por muziko</string>
<string name="use_tor_summary">(Eksperimenta) Devigi elŝuttrafikon tra Tor por pli bona privateco (elsendfluaj videoj estas ankoraŭ ne subtenitaj).</string>
<string name="show_play_with_kodi_summary">Montri opcion por ludi videon per la aplikaĵo Kodi.</string>
<string name="download_path_summary">Dosierujo por konservi elŝutitajn videojn.</string>
<string name="show_play_with_kodi_summary">Montri opcion por ludi videon per la aplikaĵo Kodi</string>
<string name="download_path_summary">Dosierujo por konservi elŝutitajn videojn</string>
<string name="download_path_audio_summary">Dosierujo por konservi elŝutitan muzikon</string>
<string name="download_path_dialog_title">Elektu lokon por konservi elŝutitajn videojn</string>
<string name="download_path_audio_dialog_title">Elektu lokon por konservi elŝutitan muzikon.</string>
<string name="download_path_audio_dialog_title">Elektu lokon por konservi elŝutitan muzikon</string>
<string name="content">Enhavo</string>
<string name="error_report_button_text">Raporti eraron per retpoŝto</string>
@@ -77,13 +77,39 @@
<string name="report_error">Raporti eraron</string>
<string name="video">Video</string>
<string name="retry">Reprovi</string>
<string name="main_bg_subtitle">Alklaku serĉon por ekkomenci</string>
<string name="main_bg_subtitle">Tapu serĉo por komenci</string>
<string name="no_player_found_toast">Neniu elsendlflua ludilo trovita (instalu VLC por ludi ĝin)</string>
<string name="open_in_popup_mode">Malferu per fenestreta maniero</string>
<string name="use_external_video_player_summary">Iuj rezolucioj NE havos aŭdion kiam ĉi tiu eblo estas ebligita.</string>
<string name="popup_mode_share_menu_title">Fenestreta maniero de NewPipe</string>
<string name="open_in_popup_mode">Malfermi en ŝprucfenestron modon</string>
<string name="use_external_video_player_summary">Iuj rezolucioj NE havos aŭdion kiam ĉi tiu eblo estas ebligita</string>
<string name="popup_mode_share_menu_title">NewPipe ŝprucfenestron modon</string>
<string name="subscribe_button_title">Aboni</string>
<string name="subscribed_button_title">Abonita</string>
<string name="channel_unsubscribed">Kanalo malabonita</string>
<string name="subscription_change_failed">Neebla ŝanĝi abonon</string>
<string name="controls_download_desc">Elŝutu dosieron.</string>
<string name="subscription_update_failed">Ne eblas ĝisdatigi abonon</string>
<string name="show_info">Montri informon</string>
<string name="tab_main">Ĉefa</string>
<string name="tab_subscriptions">Abonoj</string>
<string name="tab_bookmarks">Legosigno</string>
<string name="fragment_whats_new">Kio novas</string>
<string name="controls_background_title">Fono</string>
<string name="controls_popup_title">ŝprucfenestron</string>
<string name="controls_add_to_playlist_title">Aldonu al</string>
<string name="autoplay_by_calling_app_title">Aŭtomata play</string>
<string name="autoplay_by_calling_app_summary">Ludas video kiam NewPipe vokas de alia programo</string>
<string name="default_popup_resolution_title">Defaŭlta popup rezolucio</string>
<string name="show_higher_resolutions_title">Montri pli altajn rezoluciojn</string>
<string name="show_higher_resolutions_summary">Nur kelkaj aparatoj subtenas ludante 2K / 4K filmetojn</string>
<string name="default_video_format_title">Defaŭlta video-formato</string>
<string name="black_theme_title">Nigra</string>
<string name="popup_remember_size_pos_title">Memoru ŝprucfenestron kaj pozicion</string>
<string name="popup_remember_size_pos_summary">Memoru lastan grandecon kaj pozicion de ŝprucfenestro</string>
<string name="use_inexact_seek_title">Uzu rapide, ne preciza serĉon</string>
<string name="use_inexact_seek_summary">Ne preciza serĉo permesas al la ludanto serĉi poziciojn pli rapide kun malalta precizeco</string>
<string name="download_thumbnail_title">Ŝarĝi bildetojn</string>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="view_count_text">%1$s visualizaciones</string>
<string name="view_count_text">%1$s de visualizaciones</string>
<string name="upload_date_text">Publicado el %1$s</string>
<string name="no_player_found">No se encontró ningún reproductor de vídeo. ¿Desea instalar VLC?</string>
<string name="install">Instalar</string>
@@ -58,7 +58,7 @@
<string name="could_not_load_thumbnails">No se pudo cargar las miniaturas</string>
<string name="youtube_signature_decryption_error">No se pudo descifrar la URL del vídeo</string>
<string name="parsing_error">No se pudo analizar el sitio web</string>
<string name="show_next_and_similar_title">Mostrar vídeos siguientes y similares</string>
<string name="show_next_and_similar_title">Mostrar vídeos \'siguientes\' y \'similares\'</string>
<string name="search_language_title">Idioma del contenido por defecto</string>
<string name="list_thumbnail_view_description">Vista previa del vídeo</string>
<string name="detail_thumbnail_view_description">Vista previa del vídeo</string>
@@ -74,7 +74,7 @@
<string name="main_bg_subtitle">Toque en buscar para empezar</string>
<string name="autoplay_by_calling_app_title">Autoreproducir</string>
<string name="autoplay_by_calling_app_summary">Reproducir automáticamente un vídeo cuando NewPipe es llamado desde otra aplicación</string>
<string name="autoplay_by_calling_app_summary">Reproducir un vídeo cuando NewPipe es llamado desde otra app</string>
<string name="duration_live">en vivo</string>
<string name="downloads">Descargas</string>
<string name="downloads_title">Descargas</string>
@@ -112,7 +112,7 @@
<string name="finish">OK</string>
<string name="msg_name">Nombre del archivo</string>
<string name="msg_threads">Threads</string>
<string name="msg_threads">Hilo</string>
<string name="msg_error">Error</string>
<string name="msg_server_unsupported">Servidor no soportado</string>
<string name="msg_exists">El archivo ya existe</string>
@@ -188,7 +188,7 @@ abrir en modo popup</string>
<string name="tab_about">Acerca de</string>
<string name="tab_contributors">Colaboradores</string>
<string name="tab_licenses">Licencias</string>
<string name="app_description">Una gratuita y ligera interfaz de YouTube para Android.</string>
<string name="app_description">Streaming gratuito y ligero en Android.</string>
<string name="view_on_github">Ver en GitHub</string>
<string name="app_license_title">Licencia de NewPipe</string>
<string name="contribution_encouragement">Si tienes ideas de; traducción, cambios de diseño, limpieza de código o cambios de código realmente fuertes—la ayuda siempre es bienvenida. Cuanto más se hace, mejor se pone!</string>
@@ -218,7 +218,7 @@ abrir en modo popup</string>
<string name="enable_search_history_title">Historial de búsqueda</string>
<string name="enable_search_history_summary">Almacenar búsquedas localmente</string>
<string name="enable_watch_history_title">Historial</string>
<string name="enable_watch_history_title">Historial y caché</string>
<string name="enable_watch_history_summary">Almacenar historial de vídeos vistos</string>
<string name="title_activity_history">Historial</string>
<string name="title_history_search">Buscado</string>
@@ -278,7 +278,7 @@ abrir en modo popup</string>
<string name="popup_playing_append">En cola en el reproductor popup</string>
<string name="play_all">Reproducir todo</string>
<string name="player_stream_failure">Error al reproducir esta transmisión</string>
<string name="player_stream_failure">No se pudo reproducir este stream</string>
<string name="player_unrecoverable_failure">Se produjo un error irrecuperable del reproductor</string>
<string name="player_recoverable_failure">Recuperándose del error del reproductor</string>
@@ -298,10 +298,10 @@ abrir en modo popup</string>
<string name="new_and_hot">Nuevo y popular</string>
<string name="hold_to_append">Mantener para poner en la cola</string>
<string name="donation_title">Donar</string>
<string name="donation_encouragement">NewPipe es desarrollado por voluntarios que emplean su tiempo libre en mejorar tu experiencia. ¡ Ahora puedes devolver el favor para asegurar que los desarrolladores puedan crear un NewPipe aún mejor mientras saborean una taza de Java !</string>
<string name="donation_encouragement">NewPipe es desarrollado por voluntarios que emplean su tiempo para brindarte la mejor experiencia. Devuelve el favor para ayudar a los desarrolladores a crear un NewPipe aún mejor mientras disfrutan de una taza de café.</string>
<string name="give_back">Donar</string>
<string name="website_title">Página web</string>
<string name="website_encouragement">Para obtener más información y las últimas noticias sobre NewPipe, visita nuestra web.</string>
<string name="website_encouragement">Visita el sitio web de NewPipe para más información y noticias.</string>
<string name="default_content_country_title">País del contenido por defecto</string>
<string name="toggle_orientation">Alternar orientación</string>
<string name="switch_to_background">Cambiar a segundo plano</string>
@@ -326,7 +326,7 @@ abrir en modo popup</string>
<string name="always_ask_player">Preguntar siempre</string>
<string name="preferred_player_fetcher_notification_title">Obteniendo información…</string>
<string name="preferred_player_fetcher_notification_message">El contenido solicitado se está cargando</string>
<string name="preferred_player_fetcher_notification_message">Cargando contenido solicitado</string>
<string name="import_data_title">Importar base de datos</string>
<string name="export_data_title">Exportar base de datos</string>
<string name="import_data_summary">Reemplazará su historial actual y sus suscripciones</string>
@@ -370,23 +370,23 @@ abrir en modo popup</string>
<string name="unbookmark_playlist">Remover marcador</string>
<string name="delete_playlist_prompt">¿Desea eliminar esta lista de reproducción?</string>
<string name="playlist_creation_success">Lista de reproducción creada exitosamente</string>
<string name="playlist_creation_success">Lista de reproducción creada</string>
<string name="playlist_add_stream_success">Añadido a la lista de reproducción</string>
<string name="playlist_thumbnail_change_success">Miniatura de lista de reproducción cambiada</string>
<string name="playlist_delete_failure">Error al eliminar la lista de reproducción</string>
<string name="playlist_delete_failure">No se pudo eliminar la lista de reproducción</string>
<string name="smaller_caption_font_size">Fuente más pequeña</string>
<string name="normal_caption_font_size">Fuente normal</string>
<string name="larger_caption_font_size">Fuente más grande</string>
<string name="drawer_header_action_paceholder_text">Algo vendrá aquí pronto ;D</string>
<string name="drawer_header_action_paceholder_text">Algo aparecerá aquí pronto ;D</string>
<string name="caption_none">Sin subtítulos</string>
<string name="resize_fit">AJUSTAR</string>
<string name="resize_fill">RELLENAR</string>
<string name="resize_zoom">ZOOM</string>
<string name="resize_fit">Ajustar</string>
<string name="resize_fill">Rellenar</string>
<string name="resize_zoom">Zoom</string>
<string name="caption_font_size_settings_title">Tamaño de fuente de subtítulos</string>
<string name="toggle_leak_canary">Monitoreo de fugas</string>
@@ -395,15 +395,15 @@ abrir en modo popup</string>
<string name="settings_category_debug_title">Depuración</string>
<string name="caption_auto_generated">Auto generados</string>
<string name="enable_leak_canary_title">Activar LeakCanary</string>
<string name="enable_leak_canary_summary">La monitorización de fugas de memoria puede causar que la app no responda cuando hay volcado de almacenamiento</string>
<string name="enable_leak_canary_summary">La monitorización de fugas de memoria puede causar que la app no responda cuando hay Heap Dump</string>
<string name="enable_disposed_exceptions_title">Reportar errores fuera del ciclo de duración</string>
<string name="enable_disposed_exceptions_summary">Forzar la notificación de excepciones no entregables de RX que ocurren fuera del fragmento o del ciclo de actividad después de disponer</string>
<string name="enable_disposed_exceptions_summary">Forzar reporte de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte</string>
<string name="use_inexact_seek_title">Usar búsqueda rápida inexacta</string>
<string name="use_inexact_seek_summary">La búsqueda inexacta permite al reproductor buscar posiciones más rápido con menor precisión</string>
<string name="auto_queue_title">Auto-encolar la siguiente transmisión</string>
<string name="auto_queue_summary">Automáticamente añadir un vídeo relacionado cuando el reproductor llegue al último vídeo en una lista de reproducción no repetible.</string>
<string name="auto_queue_summary">Auto-añadir un vídeo relacionado al reproducir el último vídeo en una cola no repetitiva.</string>
<string name="live">DIRECTO</string>
<string name="live_sync">SINCRONIZAR</string>
<string name="file">Archivo</string>
@@ -425,22 +425,20 @@ abrir en modo popup</string>
<string name="import_file_title">Importar archivo</string>
<string name="previous_export">Exportación anterior</string>
<string name="subscriptions_import_unsuccessful">Importación de suscripciones fallida</string>
<string name="subscriptions_export_unsuccessful">Exportación de suscripciones fallida</string>
<string name="subscriptions_import_unsuccessful">No se pudo importar suscripciones</string>
<string name="subscriptions_export_unsuccessful">No se pudo exportar suscripciones</string>
<string name="import_youtube_instructions">Para importar sus suscripciones de YouTube, necesitará el archivo de exportación, el cual puede ser descargado siguiendo estas instrucciones:
\n
\n1. Vaya a esta URL: %1$s
\n2. Ingrese a su cuenta cuando se le pida
\n3. Una descarga debería comenzar (ese es el archivo de exportación)</string>
<string name="import_soundcloud_instructions">Para importar sus seguimientos de SoundCloud, debe conocer la URL o el ID de su perfil. Si es así, simplemente escriba cualquiera de ellos en la entrada de abajo y ya está listo para comenzar.
\n
\nSi no es así, puede seguir estos pasos:
\n
\n1. Active el \"modo escritorio\" en algún navegador (el sitio no está disponible para dispositivos móviles)
\n2. Vaya a esta URL: %1$s
\n3. Ingrese a su cuenta cuando se le pida
\n4. Copie la URL a la que fue redireccionado (esa es la URL de su perfil)</string>
<string name="import_youtube_instructions">Importe suscripciones de YouTube descargando el archivo de exportación:
\n
\n1. Vaya a esta URL: %1$s
\n2. Inicie sesión cuando se le pida
\n3. Una descarga debería empezar (ese es el archivo de exportación)</string>
<string name="import_soundcloud_instructions">Importe un perfil de SoundCloud escribiendo la URL o su ID:
\n
\n1. Active el \"modo escritorio\" en un navegador web (el sitio no está disponible para dispositivos móviles)
\n2. Vaya a esta URL: %1$s
\n3. Inicie sesión cuando se le pida
\n4. Copie la URL del perfil a la que fue redireccionado.</string>
<string name="import_soundcloud_instructions_hint">suID, soundcloud.com/suID</string>
<string name="import_network_expensive_warning">Tenga en cuenta que esta operación puede ser costosa para la red.
@@ -452,21 +450,21 @@ abrir en modo popup</string>
<string name="metadata_cache_wipe_title">Metadatos eliminados del caché</string>
<string name="metadata_cache_wipe_summary">Eliminar todos los datos de la página web en caché</string>
<string name="metadata_cache_wipe_complete_notice">Metadatos del caché limpiados</string>
<string name="playback_speed_control">Control de velocidad de la reproducción</string>
<string name="playback_speed_control">Controles de velocidad de reproducción</string>
<string name="playback_tempo">Tiempo</string>
<string name="playback_pitch">Tono</string>
<string name="unhook_checkbox">Desenganchar (puede casusar distorsión)</string>
<string name="unhook_checkbox">Desenganchar (puede causar distorsión)</string>
<string name="playback_nightcore">Nightcore (tipo de música)</string>
<string name="playback_default">Reproducción por defecto</string>
<string name="no_streams_available_download">No hay streams disponibles para descargar</string>
<string name="preferred_open_action_settings_title">Acción de abrir preferida</string>
<string name="preferred_open_action_settings_title">Acción \'abrir\' preferida</string>
<string name="preferred_open_action_settings_summary">Acción por defecto al abrir contenido — %s</string>
<string name="toast_no_player">Ningún reproductor ha sido encontrado para este archivo</string>
<string name="toast_no_player">No hay ninguna app instalada para reproducir este archivo</string>
<string name="caption_setting_title">Subtítulos</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos del reproductor y los estilos de fondo. Requiere un reinicio del reproductor para que surta efecto.</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos y los estilos de fondo del reproductor. Requiere el reinicio de la app para que surta efecto.</string>
<string name="clear_views_history_title">Borrar historial de reproducciones</string>
<string name="clear_views_history_summary">Elimina el historial de las transmisiones reproducidas.</string>
@@ -478,4 +476,26 @@ abrir en modo popup</string>
<string name="search_history_deleted">Historial de búsquedas eliminado.</string>
<string name="one_item_deleted">1 elemento eliminado.</string>
<string name="app_license">NewPipe es un software copyleft libre: puedes usarlo, estudiarlo, compartirlo y mejorarlo a tu antojo. Específicamente, puedes redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General GNU publicada por la Free Software Foundation, ya sea la versión 3 de la Licencia, o (a tu elección) cualquier versión posterior.</string>
<string name="import_settings">¿Desea importar también los ajustes?</string>
<string name="privacy_policy_title">Política de Privacidad de NewPipe</string>
<string name="privacy_policy_encouragement">El proyecto NewPipe toma su privacidad muy en serio. Por lo tanto, la aplicación no recopila ningún dato sin su consentimiento. La política de privacidad de NewPipe explica en detalle qué datos se envían y almacenan cuando envía un informe de fallas.</string>
<string name="read_privacy_policy">Leer la Política de Privacidad</string>
<string name="start_accept_privacy_policy">Para cumplir con el Reglamento general europeo de protección de datos (GDPR), podemos llamar su atención sobre la política de privacidad de NewPipe. Por favor léelo cuidadosamente. Debe aceptarlo para enviarnos el informe de error.</string>
<string name="accept">Aceptar</string>
<string name="decline">Declinar</string>
<string name="limit_data_usage_none_description">Sin límite</string>
<string name="limit_mobile_data_usage_title">Limitar resolución cuando se use Datos Móviles</string>
<string name="minimize_on_exit_title">Mimimizar al cambiar de aplicación</string>
<string name="minimize_on_exit_summary">Acción de cambiar a otra aplicación desde el reproductor principal</string>
<string name="minimize_on_exit_none_description">Ninguna</string>
<string name="minimize_on_exit_background_description">Minimizar al reproductor de fondo</string>
<string name="minimize_on_exit_popup_description">Minimizar el reproductor emergente</string>
<string name="skip_silence_checkbox">Avance rápido durante el silencio</string>
<string name="playback_step">Paso</string>
<string name="playback_reset">Reiniciar</string>
</resources>

View File

@@ -0,0 +1,421 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Alustuseks puuduta otsingut</string>
<string name="view_count_text">%1$s vaatamist</string>
<string name="upload_date_text">Avaldatud %1$s</string>
<string name="no_player_found">Voogesituseks puudub pleier. Kas paigaldada VLC?</string>
<string name="no_player_found_toast">Voogesituseks puudub pleier. (Selleks võib paigaldada VLC)</string>
<string name="install">Paigalda</string>
<string name="cancel">Tühista</string>
<string name="open_in_browser">Ava veebilehitsejas</string>
<string name="open_in_popup_mode">Ava hüpikaknas</string>
<string name="share">Jaga</string>
<string name="download">Laadi alla</string>
<string name="controls_download_desc">Laadi voog alla.</string>
<string name="search">Otsi</string>
<string name="settings">Seaded</string>
<string name="did_you_mean">Kas mõtlesid: %1$s ?</string>
<string name="share_dialog_title">Jaga</string>
<string name="choose_browser">Vali veebilehitseja</string>
<string name="screen_rotation">pööramine</string>
<string name="use_external_video_player_title">Kasuta välist videopleierit</string>
<string name="use_external_video_player_summary">Kui see valik lubada, puudub osadel lahutustel heli</string>
<string name="use_external_audio_player_title">Kasuta välist audiopleierit</string>
<string name="popup_mode_share_menu_title">NewPipe hüpikaknarežiim</string>
<string name="subscribe_button_title">Telli</string>
<string name="subscribed_button_title">Tellitud</string>
<string name="channel_unsubscribed">Kanali tellimus tühistatud</string>
<string name="subscription_change_failed">Tellimuse muutmine nurjus</string>
<string name="subscription_update_failed">Tellimuse uuendamine nurjus</string>
<string name="show_info">Kuva info</string>
<string name="tab_subscriptions">Tellimused</string>
<string name="tab_bookmarks">Järjehoidjad</string>
<string name="fragment_whats_new">Mis on uut</string>
<string name="controls_background_title">Taust</string>
<string name="controls_popup_title">Hüpikaken</string>
<string name="controls_add_to_playlist_title">"Lisa "</string>
<string name="download_path_title">Video allalaadimise kaust</string>
<string name="download_path_summary">Kaust allalaetud videote hoiustamiseks</string>
<string name="download_path_dialog_title">Sisesta videote allalaadimise rada</string>
<string name="download_path_audio_title">Audio allalaadimise kaust</string>
<string name="download_path_audio_summary">Kaust allalaetud audio hoiustamiseks</string>
<string name="download_path_audio_dialog_title">Sisesta audio allalaadimise rada</string>
<string name="autoplay_by_calling_app_title">Automaatesitus</string>
<string name="autoplay_by_calling_app_summary">Esita video, kui NewPipe käivitub teise rakenduse kaudu</string>
<string name="default_resolution_title">Vaikelahutus</string>
<string name="default_popup_resolution_title">Hüpikakna vaikelahutus</string>
<string name="show_higher_resolutions_title">Kuva kõrgemaid lahutusi</string>
<string name="show_higher_resolutions_summary">Kõik seadmed ei toeta 2K/4K videoid</string>
<string name="play_with_kodi_title">Esita Kodi abil</string>
<string name="kore_not_found">Kore rakendust ei leitud. Kas paigaldada see?</string>
<string name="show_play_with_kodi_title">Kuva valik \"Esita Kodi abil\"</string>
<string name="show_play_with_kodi_summary">Kuva valik video esitamiseks Kodi meediakeskuse kaudu</string>
<string name="play_audio">Heli</string>
<string name="default_audio_format_title">Heli vaikevorming</string>
<string name="default_video_format_title">Video vaikevorming</string>
<string name="webm_description">WebM — libre vorming</string>
<string name="m4a_description">M4A — parem kvaliteet</string>
<string name="theme_title">Teema</string>
<string name="light_theme_title">Hele</string>
<string name="dark_theme_title">Tume</string>
<string name="black_theme_title">Must</string>
<string name="popup_remember_size_pos_title">Pea hüpikakna suurus ja asukoht meeles</string>
<string name="popup_remember_size_pos_summary">Pea hüpikakna viimane suurus ja asukoht meeles</string>
<string name="use_inexact_seek_title">Kasuta ebatäpset kerimist</string>
<string name="use_inexact_seek_summary">Ebatäpne kerimine lubab pleieril otsida asukohta kiiremini täpsuse arvel</string>
<string name="download_thumbnail_title">Laadi pisipildid</string>
<string name="download_thumbnail_summary">Keela, peatamaks pisipiltide laadimine ja vähenda andme- ja mälukasutust. Selle muutmine puhastab nii sisemälu kui piltide vahemälu andmekandjal.</string>
<string name="thumbnail_cache_wipe_complete_notice">Pildid kustutati vahemälust</string>
<string name="metadata_cache_wipe_title">Kustuta metaandmed vahemälust</string>
<string name="metadata_cache_wipe_summary">Kustuta veebilehtede andmed vahemälust</string>
<string name="metadata_cache_wipe_complete_notice">Metaandmed kustutati vahemälust</string>
<string name="auto_queue_title">Järgmine voog automaatselt järjekorda</string>
<string name="auto_queue_summary">Lisa seotud voog automaatselt, kui esitusel on viimane voog mittekorduvast järjekorrast.</string>
<string name="player_gesture_controls_title">Pleieri juhtimise viiped</string>
<string name="player_gesture_controls_summary">Luba viiped helitugevuse ja ereduse juhtimiseks</string>
<string name="show_search_suggestions_title">Kuva soovitused</string>
<string name="show_search_suggestions_summary">Kuva otsingu ajal soovitusi</string>
<string name="enable_search_history_title">Otsinguajalugu</string>
<string name="enable_search_history_summary">Salvesta otsinguajalugu kohalikult</string>
<string name="enable_watch_history_title">Ajalugu ja vahemälu</string>
<string name="enable_watch_history_summary">Jälgi videote vaatamist</string>
<string name="resume_on_audio_focus_gain_title">Jätka taasesitust fookuse saamisel</string>
<string name="resume_on_audio_focus_gain_summary">Jätka taasesitust pärast katkestamist (nt. telefonikõne)</string>
<string name="download_dialog_title">Laadi alla</string>
<string name="next_video_title">Järgmine video</string>
<string name="show_next_and_similar_title">Kuva \'järgmine\' ja \'sarnased\' videod</string>
<string name="show_hold_to_append_title">Kuva vihjet \"lisamiseks hoia\"</string>
<string name="show_hold_to_append_summary">Kuva vihje, kui videoandmete lehel vajutatakse tausta või hüpikakna nupule</string>
<string name="url_not_supported_toast">URL pole toetatud</string>
<string name="default_content_country_title">Sisu vaikimisi riik</string>
<string name="service_title">Teenus</string>
<string name="search_language_title">Sisu vaikimisi keel</string>
<string name="settings_category_player_title">Mängija</string>
<string name="settings_category_player_behavior_title">Käitumine</string>
<string name="settings_category_video_audio_title">Heli ja pilt</string>
<string name="settings_category_history_title">Ajalugu ja vahemälu</string>
<string name="settings_category_popup_title">Hüpikaken</string>
<string name="settings_category_appearance_title">Välimus</string>
<string name="settings_category_other_title">Muu</string>
<string name="settings_category_debug_title">Silumine</string>
<string name="background_player_playing_toast">Taasesitus taustal</string>
<string name="popup_playing_toast">Taasesitus hüpikaknas</string>
<string name="background_player_append">Lisati taustapleieri järjekorda</string>
<string name="popup_playing_append">Lisati hüpikpleieri järjekorda</string>
<string name="play_btn_text">Esita</string>
<string name="content">Sisu</string>
<string name="show_age_restricted_content_title">Kuva vanusepiiranguga sisu</string>
<string name="video_is_age_restricted">Vanusepiiranguga sisu. Selle saab lubada seadetes.</string>
<string name="duration_live">Otse</string>
<string name="downloads">Allalaadimised</string>
<string name="downloads_title">Allalaadimised</string>
<string name="error_report_title">Vea teatamine</string>
<string name="all">Kõik</string>
<string name="channel">Kanal</string>
<string name="playlist">Pleilist</string>
<string name="yes">Jah</string>
<string name="later">Hiljem</string>
<string name="disabled">Keelatud</string>
<string name="filter">Filter</string>
<string name="refresh">Värskenda</string>
<string name="clear">Kustuta</string>
<string name="popup_resizing_indicator_title">Suuruse muutmine</string>
<string name="best_resolution">Parim lahutus</string>
<string name="undo">Võta tagasi</string>
<string name="play_all">Esita kõik</string>
<string name="always">Alati</string>
<string name="just_once">Üks kord</string>
<string name="file">Fail</string>
<string name="notification_channel_name">NewPipe teavitus</string>
<string name="notification_channel_description">Teavitused NewPipe tausta- ja hüpikpleierile</string>
<string name="unknown_content">[Tundmatu]</string>
<string name="toggle_orientation">Vaheta suunda</string>
<string name="switch_to_background">Lülita taustale</string>
<string name="switch_to_popup">Lülita hüpikpleierile</string>
<string name="import_data_title">Impordi andmebaas</string>
<string name="export_data_title">Ekspordi andmebaas</string>
<string name="import_data_summary">Alistab praeguse ajaloo ja tellimused</string>
<string name="export_data_summary">Ekspordi ajalugu, tellimused ja pleilistid.</string>
<string name="clear_views_history_title">Puhasta vaatamiste ajalugu</string>
<string name="clear_views_history_summary">Kustutab vaadatud voogude ajaloo.</string>
<string name="delete_view_history_alert">Kustuta kogu vaatamiste ajalugu.</string>
<string name="view_history_deleted">Vaatamiste ajalugu kustutati.</string>
<string name="clear_search_history_title">Kustuta otsinguajalugu</string>
<string name="clear_search_history_summary">Kustutab otsisõnade ajaloo.</string>
<string name="delete_search_history_alert">Kustuta kogu otsinguajalugu.</string>
<string name="search_history_deleted">Otsinguajalugu kustutati.</string>
<string name="general_error">Viga</string>
<string name="network_error">Võrgu viga</string>
<string name="could_not_load_thumbnails">Kõiki pisipilte ei õnnestunud laadida</string>
<string name="youtube_signature_decryption_error">Video URLi dekrüptimine nurjus</string>
<string name="parsing_error">Veebilehe töötlemine nurjus</string>
<string name="light_parsing_error">Veebilehe täielik töötlemine nurjus</string>
<string name="content_not_available">Sisu pole saadaval</string>
<string name="blocked_by_gema">GEMA poolt blokeeritud</string>
<string name="could_not_setup_download_menu">Allalaadimismenüü seadistamine nurjus</string>
<string name="live_streams_not_supported">See on OTSEVOOG, mis pole veel toetatud.</string>
<string name="could_not_get_stream">Ühtegi voogu ei leitud</string>
<string name="could_not_load_image">Pildi laadimine nurjus</string>
<string name="app_ui_crash">Rakendus jooksis kokku</string>
<string name="player_stream_failure">Selle voo esitus nurjus</string>
<string name="player_unrecoverable_failure">Ilmnes taastamatu pleieri viga</string>
<string name="player_recoverable_failure">Pleieri veast taastumine</string>
<string name="external_player_unsupported_link_type">Välised pleierid ei toeta seda tüüpi linke</string>
<string name="invalid_url_toast">Kehtetu URL</string>
<string name="video_streams_empty">Videovooge ei leitud</string>
<string name="audio_streams_empty">Helivooge ei leitud</string>
<string name="invalid_directory">Kehtetu kataloog</string>
<string name="invalid_source">Kehtetu faili/sisu allikas</string>
<string name="invalid_file">Fail puudub või puuduvad õigused seda lugeda või kirjutada</string>
<string name="file_name_empty_error">Tühi failinimi pole lubatud</string>
<string name="error_occurred_detail">Ilmnes viga: %1$s</string>
<string name="no_streams_available_download">Allalaaditavaid videovooge pole</string>
<string name="sorry_string">Vabandust, seda poleks pidanud juhtuma.</string>
<string name="error_report_button_text">Teata veast e-posti kaudu</string>
<string name="error_snackbar_message">Vabandust, ilmnesid mõned vead.</string>
<string name="error_snackbar_action">TEATA</string>
<string name="what_device_headline">Info:</string>
<string name="what_happened_headline">Mis juhtus:</string>
<string name="info_labels">Mis:\\nPäring:\\nSisu Keel:\\nTeenus:\\nGMT aeg:\\nPakett:\\nVersioon:\\nOS versioon:</string>
<string name="your_comment">Oma kommentaar (inglise keeles):</string>
<string name="error_details_headline">Üksikasjad:</string>
<string name="list_thumbnail_view_description">Video eelvaate pisipilt</string>
<string name="detail_thumbnail_view_description">Video eelvaate pisipilt</string>
<string name="detail_uploader_thumbnail_view_description">Üleslaadiaja avatari pisipilt</string>
<string name="detail_likes_img_view_description">Meeldib</string>
<string name="detail_dislikes_img_view_description">Ei meeldi</string>
<string name="use_tor_title">Kasuta Tor võrku</string>
<string name="use_tor_summary">(Eksperimentaalne) Kasuta allalaadimiseks Tor võrku, et suurendada privaatsust (voogesitus ei ole veel toetatud).</string>
<string name="report_error">Teata veast</string>
<string name="user_report">Kasutaja raport</string>
<string name="search_no_results">Tulemusi pole</string>
<string name="empty_subscription_feed_subtitle">Siin pole veel midagi</string>
<string name="detail_drag_description">Lohista järjestuse muutmiseks</string>
<string name="err_dir_create">Allalaadimiskataloogi \'%1$s\' loomine nurjus</string>
<string name="info_dir_created">Loodi allalaadimiskataloog \'%1$s\'</string>
<string name="video">Video</string>
<string name="audio">Audio</string>
<string name="retry">Proovi uuesti</string>
<string name="storage_permission_denied">Pääsuõigused salvestile puuduvad</string>
<string name="use_old_player_title">Kasuta vana mängijat</string>
<string name="use_old_player_summary">Vana sisseehitatud mediaframework pleier</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="no_subscribers">Tellijaid pole</string>
<plurals name="subscribers">
<item quantity="one">%s tellija</item>
<item quantity="other">%s tellijat</item>
</plurals>
<string name="no_views">Pole vaadatud</string>
<plurals name="views">
<item quantity="one">%s vaatamine</item>
<item quantity="other">%s vaatamist</item>
</plurals>
<string name="no_videos">Videoid pole</string>
<plurals name="videos">
<item quantity="one">%s video</item>
<item quantity="other">%s videot</item>
</plurals>
<string name="start">"Start "</string>
<string name="pause">Paus</string>
<string name="view">Esita</string>
<string name="create">Loo</string>
<string name="delete">Kustuta</string>
<string name="delete_one">Kustuta üks</string>
<string name="delete_all">Kustuta kõik</string>
<string name="checksum">Kontrollsumma</string>
<string name="dismiss">Loobu</string>
<string name="rename">Nimeta ümber</string>
<string name="add">Uus ülesanne</string>
<string name="finish">OK</string>
<string name="msg_name">Faili nimi</string>
<string name="msg_threads">Lõimed</string>
<string name="msg_error">Viga</string>
<string name="msg_server_unsupported">Server pole toetatud</string>
<string name="msg_exists">Fail on juba olemas</string>
<string name="msg_url_malform">Vigane URL või internet pole saadaval</string>
<string name="msg_running">NewPipe allalaadimine</string>
<string name="msg_running_detail">Puuduta üksikasjade nägemiseks</string>
<string name="msg_wait">Palun oota…</string>
<string name="msg_copied">Kopeeriti lõikepuhvrisse</string>
<string name="no_available_dir">Vali saadaolev allalaadimiste kataloog</string>
<string name="msg_popup_permission">Need õigused on vajalikud
\nhüpikakna avamiseks</string>
<string name="one_item_deleted">Kustutati 1 element.</string>
<string name="reCaptchaActivity">"reCAPTCHA "</string>
<string name="settings_category_downloads_title">Laadi alla</string>
<string name="settings_file_charset_title">Lubatud tähemärgid failinimedes</string>
<string name="settings_file_replacement_character_summary">Vigased tähemärgid asendatakse selle väärtusega</string>
<string name="settings_file_replacement_character_title">Asendustähemärk</string>
<string name="charset_letters_and_digits">Tähed ja numbrid</string>
<string name="charset_most_special_characters">Erimärgid</string>
<string name="toast_no_player">Selle faili esitamiseks puudub rakendus</string>
<string name="title_activity_about">NewPipe rakendusest</string>
<string name="action_settings">Seaded</string>
<string name="action_about">Programmist</string>
<string name="title_licenses">Kolmanda osapoole litsentsid</string>
<string name="error_unable_to_load_license">Litsentsi laadimine nurjus</string>
<string name="action_open_website">Ava veebileht</string>
<string name="tab_about">Programmist</string>
<string name="tab_contributors">Kaasautorid</string>
<string name="tab_licenses">Litsentsid</string>
<string name="contribution_title">Panusta</string>
<string name="view_on_github">Vaata GitHubis</string>
<string name="donation_title">Anneta</string>
<string name="website_title">Veebileht</string>
<string name="website_encouragement">Enama info saamiseks külasta NewPipe veebilehte.</string>
<string name="privacy_policy_title">NewPipe privaatsuspoliitika</string>
<string name="read_privacy_policy">Loe privaatsuspoliitikat</string>
<string name="app_license_title">NewPipe litsents</string>
<string name="read_full_license">Loe litsentsi</string>
<string name="title_activity_history">Ajalugu</string>
<string name="title_history_search">Otsitud</string>
<string name="title_history_view">Vaadatud</string>
<string name="history_disabled">Ajalugu on keelatud</string>
<string name="action_history">Ajalugu</string>
<string name="history_empty">Ajalugu on tühi</string>
<string name="history_cleared">Ajalugu kustutati</string>
<string name="item_deleted">Element kustutati</string>
<string name="delete_item_search_history">Kas kustutada see kirje otsinguajaloost?</string>
<string name="delete_stream_history_prompt">Kas kustutada see kirje vaatamiste ajaloost?</string>
<string name="delete_all_history_prompt">Kas kustutada kõik kirjed ajaloost?</string>
<string name="title_last_played">Viimati esitatud</string>
<string name="title_most_played">Enim esitatud</string>
<string name="main_page_content">Avalehe sisu</string>
<string name="blank_page_summary">Tühi leht</string>
<string name="kiosk_page_summary">Kioski leht</string>
<string name="subscription_page_summary">Tellimuse leht</string>
<string name="feed_page_summary">Voo leht</string>
<string name="channel_page_summary">Kanali leht</string>
<string name="select_a_channel">Vali kanal</string>
<string name="no_channel_subscribed_yet">Ühtki kanalit pole veel tellitud</string>
<string name="select_a_kiosk">Vali kiosk</string>
<string name="export_complete_toast">Eksport tehtud</string>
<string name="import_complete_toast">Import tehtud</string>
<string name="no_valid_zip_file">Korralikku ZIP faili pole</string>
<string name="could_not_import_all_files">Hoiatus: Kõiki faile ei õnnestunud importida.</string>
<string name="override_current_data">See alistab praeguse seadistuse.</string>
<string name="import_settings">Kas importida ka seadistused?</string>
<string name="kiosk">"Kiosk "</string>
<string name="trending">Trendid</string>
<string name="top_50">"Top 50 "</string>
<string name="new_and_hot">Uus ja kuum</string>
<string name="title_activity_background_player">Taustapleier</string>
<string name="title_activity_popup_player">Hüpikpleier</string>
<string name="play_queue_remove">Eemalda</string>
<string name="play_queue_stream_detail">Üksikasjad</string>
<string name="play_queue_audio_settings">Heli seaded</string>
<string name="hold_to_append">Hoia järjekorda lisamiseks</string>
<string name="enqueue_on_background">Lisa järjekorda taustal</string>
<string name="enqueue_on_popup">Lisa järjekorda hüpikaknaga</string>
<string name="start_here_on_main">Alusta taasesitust siit</string>
<string name="start_here_on_background">Alusta siit taustal</string>
<string name="start_here_on_popup">Alusta siit hüpikaknaga</string>
<string name="drawer_open">Ava sahtel</string>
<string name="drawer_close">Sulge sahtel</string>
<string name="drawer_header_action_paceholder_text">Siia ilmub varsti midagi ;D</string>
<string name="preferred_open_action_settings_title">Eelistatud \'ava\' toiming</string>
<string name="preferred_open_action_settings_summary">Vaikimisi tegevus sisu avamisel — %s</string>
<string name="video_player">Videopleier</string>
<string name="background_player">Taustapleier</string>
<string name="popup_player">Hüpikpleier</string>
<string name="always_ask_open_action">Küsi alati</string>
<string name="preferred_player_fetcher_notification_title">Info hankimine…</string>
<string name="preferred_player_fetcher_notification_message">Soovitud sisu laadimine</string>
<string name="create_playlist">Loo uus pleilist</string>
<string name="delete_playlist">Kustuta pleilist</string>
<string name="rename_playlist">Nimeta pleilist ümber</string>
<string name="playlist_name_input">Nimi</string>
<string name="append_playlist">Lisa pleilisti</string>
<string name="set_as_playlist_thumbnail">Määra pleilisti pisipildiks</string>
<string name="bookmark_playlist">Lisa pleilist järjehoidjaks</string>
<string name="unbookmark_playlist">Eemalda järjehoidja</string>
<string name="delete_playlist_prompt">Kas kustutada see pleilist?</string>
<string name="playlist_creation_success">Pleilist loodud</string>
<string name="playlist_add_stream_success">Lisati pleilisti</string>
<string name="playlist_thumbnail_change_success">Pleilisti pisipilt muudetud</string>
<string name="playlist_delete_failure">Pleilisti kustutamine nurjus</string>
<string name="caption_none">Subtiitriteta</string>
<string name="resize_fit">Mahuta</string>
<string name="resize_fill">Täida</string>
<string name="resize_zoom">Suumi</string>
<string name="caption_auto_generated">Automaatselt loodud</string>
<string name="caption_setting_title">Subtiitrid</string>
<string name="caption_setting_description">Kohanda pleieri subtiitrite teksti suurust ja tausta. Jõustamiseks tuleb rakendus taaskäivitada.</string>
<string name="enable_leak_canary_summary">Mälulekke seire võib põhjustada rakenduse hangumise</string>
<string name="import_export_title">Import/eksport</string>
<string name="import_title">Import</string>
<string name="import_from">Impordi asukohast</string>
<string name="export_to">Ekspordi asukohta</string>
<string name="import_ongoing">Import…</string>
<string name="export_ongoing">Eksport…</string>
<string name="import_file_title">Impordi fail</string>
<string name="previous_export">Eelmine eksport</string>
<string name="subscriptions_import_unsuccessful">Tellimuste import nurjus</string>
<string name="subscriptions_export_unsuccessful">Tellimuste eksport nurjus</string>
<string name="import_youtube_instructions">Impordi YouTube tellimused eksportfaili abil:
\n
\n1. Ava URL: %1$s
\n2. Logi sisse
\n3. Allalaadimine peaks algama (see on eksportfail)</string>
<string name="import_network_expensive_warning">See toiming võib põhjustada suurt võrguliiklust.
\n
\nKas jätkata?</string>
<string name="playback_speed_control">Taasesituse kiiruse juhtimine</string>
<string name="playback_tempo">"Tempo "</string>
<string name="playback_default">Vaikimisi</string>
<string name="accept">Nõustu</string>
<string name="decline">Keeldu</string>
<string name="limit_data_usage_none_description">Piiranguta</string>
<string name="limit_mobile_data_usage_title">Piira lahutust mobiilse andmeside kasutamisel</string>
</resources>

View File

@@ -22,11 +22,11 @@
<string name="show_play_with_kodi_summary">Erakutsi bideoa Kodi multimedia zentroarekin erreproduzitzeko aukera</string>
<string name="play_audio">Audioa</string>
<string name="default_audio_format_title">Audio formatu lehenetsia</string>
<string name="webm_description">WebM — formatu askea</string>
<string name="webm_description">WebM — formatu librea</string>
<string name="m4a_description">M4A — kalitate hobea</string>
<string name="download_dialog_title">Deskargatu</string>
<string name="next_video_title">Hurrengo bideoa</string>
<string name="show_next_and_similar_title">Erakutsi hurrengo bideoa eta antzekoak</string>
<string name="show_next_and_similar_title">Erakutsi \'hurrengo\' eta \'antzeko\' bideoak</string>
<string name="url_not_supported_toast">URLak ez du euskarririk</string>
<string name="search_language_title">Edukiaren hizkuntz lehenetsia</string>
<string name="settings_category_video_audio_title">Bideoa eta Audioa</string>
@@ -50,7 +50,7 @@
<string name="download_path_audio_summary">Deskargatutako audioa gordetzeko bide-izena</string>
<string name="autoplay_by_calling_app_title">Erreprodukzio automatikoa</string>
<string name="autoplay_by_calling_app_summary">Automatikoki jotzen da bideoa NewPipe beste aplikazio batek deitu badu</string>
<string name="autoplay_by_calling_app_summary">Bideoa abiatzen du NewPipe beste aplikazio batek deitu badu</string>
<string name="dark_theme_title">Iluna</string>
<string name="light_theme_title">Argia</string>
@@ -114,7 +114,7 @@
<string name="error_snackbar_action">TXOSTENA</string>
<string name="what_device_headline">Informazioa:</string>
<string name="what_happened_headline">Zer gertatu da:</string>
<string name="info_labels">Zer:\\nEskaria:\\nEdukiaren hizkuntza:\\nZerbitzua:\\nGMT Ordua:\\nPaketea:\\nBertsioa:\\nSE bertsioa:\\nIP barruti orokorra:</string>
<string name="info_labels">Zer:\\nEskaria:\\nEdukiaren hizkuntza:\\nZerbitzua:\\nGMT Ordua:\\nPaketea:\\nBertsioa:\\nSE bertsioa:</string>
<string name="your_comment">Zure iruzkina (Ingelesez):</string>
<string name="error_details_headline">Xehetasunak:</string>
@@ -174,7 +174,7 @@
<string name="tab_about">Honi buruz</string>
<string name="tab_contributors">Parte hartzaileak</string>
<string name="tab_licenses">Lizentziak</string>
<string name="app_description">Youtube Androiden ikusteko aplikazio libre eta arina.</string>
<string name="app_description">Androiderako streaming libre eta arina.</string>
<string name="view_on_github">Ikusi GitHub zerbitzarian</string>
<string name="app_license_title">NewPipe Lizentzia</string>
<string name="contribution_encouragement">Ideiak, itzulpenak, diseinu aldaketak, kode garbiketak, kode aldaketa sakonak badituzu, laguntza beti da ongi etorria. Eginaz hobetzen da!</string>
@@ -204,12 +204,12 @@
<string name="enable_search_history_title">Bilaketa historiala</string>
<string name="enable_search_history_summary">Gorde bilaketak lokalki</string>
<string name="enable_watch_history_title">Historiala</string>
<string name="enable_watch_history_title">Historiala eta katxea</string>
<string name="enable_watch_history_summary">Gorde ikusitako bideoen historiala</string>
<string name="notification_channel_name">NewPipe jakinarazpena</string>
<string name="settings_category_player_title">Erreproduzigailua</string>
<string name="settings_category_player_behavior_title">Portaera</string>
<string name="settings_category_history_title">Historiala</string>
<string name="settings_category_history_title">Historiala eta cache-a</string>
<string name="playlist">Erreprodukzio-zerrenda</string>
<string name="undo">Desegin</string>
@@ -244,7 +244,7 @@
<string name="history_empty">Historiala hutsik dago</string>
<string name="history_cleared">Historiala garbitu da</string>
<string name="item_deleted">Elementua ezabatuta</string>
<string name="show_hold_to_append_title">Erakutsi \"Mantendu eransteko\" aholkua</string>
<string name="show_hold_to_append_title">Erakutsi \"mantendu eransteko\" aholkua</string>
<string name="show_hold_to_append_summary">Erakutsi aholkua bigarren planoko eta laster-leihoko botoia sakatzean bideoaren xehetasunen orrian</string>
<string name="default_content_country_title">Lehenetsitako edukiaren herrialdea</string>
<string name="service_title">Zerbitzua</string>
@@ -259,15 +259,15 @@
<string name="switch_to_popup">Aldatu laster-leihora</string>
<string name="switch_to_main">Aldatu nagusira</string>
<string name="player_stream_failure">Huts egin du jario hau erreproduzitzean</string>
<string name="player_stream_failure">Ezin izan da jario hau erreproduzitu</string>
<string name="player_unrecoverable_failure">Erreproduzigailuaren errore berreskuraezina gertatu da</string>
<string name="player_recoverable_failure">Erreproduzigailuaren erroretik berreskuratzen</string>
<string name="donation_title">Dohaintza</string>
<string name="donation_encouragement">NewPipe bere denbora librea zuri esperientziarik onena ekartzeko ematen duten boluntarioek garatzen dute. Orain zerbait atzera emateko unea da, garatzaileek NewPipe hobetu ahal izan dezaten Javako kafe bat hartzen duten bitartean!</string>
<string name="donation_encouragement">NewPipe zuri esperientziarik onena ekartzeko denbora ematen duten boluntarioek garatzen dute. Emaiezu zerbait garatzaileei NewPipe kafe bat hartzen duten bitartean hobetu ahal izan dezaten.</string>
<string name="give_back">Egin dohaintza</string>
<string name="website_title">Webgunea</string>
<string name="website_encouragement">NewPipe aplikazioari buruzko informazio gehiago eta azken berriak jasotzeko bisitatu gure webgunea.</string>
<string name="website_encouragement">Bisitatu NewPipe webgunea informazio gehiagorako eta berriak irakurtzeko.</string>
<string name="delete_item_search_history">Elementu hau bilaketen historialetik ezabatu nahi duzu?</string>
<string name="main_page_content">Orri nagusiko edukia</string>
@@ -313,5 +313,173 @@
<string name="always_ask_player">Galdetu beti</string>
<string name="preferred_player_fetcher_notification_title">Informazioa eskuratzen…</string>
<string name="preferred_player_fetcher_notification_message">Eskatutako edukia kargatzen ari da</string>
</resources>
<string name="preferred_player_fetcher_notification_message">Kargatzen eskatutako edukia</string>
<string name="controls_download_desc">Deskargatu jario fitxategia.</string>
<string name="show_info">Erakutsi informazioa</string>
<string name="tab_bookmarks">Gogokoak</string>
<string name="controls_add_to_playlist_title">Gehitu hona</string>
<string name="use_inexact_seek_title">Erabili bilaketa azkar ez zehatza</string>
<string name="download_thumbnail_title">Kargatu iruditxoak</string>
<string name="thumbnail_cache_wipe_complete_notice">Irudien cache-a ezabatuta</string>
<string name="metadata_cache_wipe_title">Ezabatu cache-ko metadatuak</string>
<string name="metadata_cache_wipe_summary">Kendu cache-ko wegbuneen datu guztiak</string>
<string name="metadata_cache_wipe_complete_notice">Metadatuen cache-a ezabatuta</string>
<string name="auto_queue_title">Gehitu ilarara hurrengo jarioa</string>
<string name="auto_queue_summary">Gehitu erlazionatutako jario bat azken jarioa jo bitartean errepikapenik gabeko ilara batean.</string>
<string name="settings_category_debug_title">Arazketa</string>
<string name="file">Fitxategia</string>
<string name="import_data_title">Inportatu datu-basea</string>
<string name="export_data_title">Esportatu datu-basea</string>
<string name="import_data_summary">Zure uneko historiala eta harpidetzak gainidatziko ditu</string>
<string name="export_data_summary">Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak.</string>
<string name="clear_views_history_title">Garbitu ikusitakoaren historiala</string>
<string name="clear_views_history_summary">Jotako jarioen historiala ezabatzen du.</string>
<string name="delete_view_history_alert">Ezabatu ikusitakoaren historial osoa.</string>
<string name="view_history_deleted">Ikusitakoaren historiala ezabatuta.</string>
<string name="clear_search_history_title">Garbitu bilaketa historiala</string>
<string name="clear_search_history_summary">Ezabatu bilaketa gakoen historiala.</string>
<string name="delete_search_history_alert">Ezabatu bilaketen historial osoa.</string>
<string name="search_history_deleted">Bilaketen historiala ezabatuta.</string>
<string name="invalid_directory">Direktorio baliogabea</string>
<string name="invalid_source">Fitxategi edo edukiaren iturri baliogabea</string>
<string name="invalid_file">Fitxategia ez dago edo ez dago baimen nahiko berau irakurri edo idazteko</string>
<string name="file_name_empty_error">Fitxategi izena ezin da hutsik egon</string>
<string name="error_occurred_detail">Errore bat gertatu da: %1$s</string>
<string name="no_streams_available_download">Ez dago jariorik deskargatzeko eskuragarri</string>
<string name="detail_drag_description">Arrastatu ordena aldatzeko</string>
<string name="create">Sortu</string>
<string name="delete_one">Ezabatu bat</string>
<string name="delete_all">Ezabatu guztiak</string>
<string name="dismiss">Baztertu</string>
<string name="rename">Aldatu izena</string>
<string name="one_item_deleted">Elementu 1 ezabatuta.</string>
<string name="toast_no_player">Ez dago fitxategi hau erreproduzitzeko aplikaziorik instalatuta</string>
<string name="delete_stream_history_prompt">Elementu hau ikusitakoen historialetik ezabatu nahi duzu?</string>
<string name="delete_all_history_prompt">Ziur elementu guztiak ezabatu nahi dituzula historialetik?</string>
<string name="title_last_played">Jotako azkena</string>
<string name="title_most_played">Ikusiena</string>
<string name="export_complete_toast">Esportazioa burututa</string>
<string name="import_complete_toast">Inportazioa burututa</string>
<string name="no_valid_zip_file">Ez da baliozko ZIP fitxategia</string>
<string name="could_not_import_all_files">Ebisua: Ezin izan dira fitxategi guztiak inportatu.</string>
<string name="override_current_data">Honek oraingo ezarpenak gainidatziko ditu.</string>
<string name="drawer_header_action_paceholder_text">Laster hemen zerbait egongo da ;D</string>
<string name="preferred_open_action_settings_title">\'Ireki\' ekintza hobetsia</string>
<string name="preferred_open_action_settings_summary">Lehenetsitako ekintza edukia irekitzean — %s</string>
<string name="always_ask_open_action">Galdetu beti</string>
<string name="create_playlist">Sortu erreprodukzio-zerrenda berria</string>
<string name="delete_playlist">Ezabatu erreprodukzio-zerrenda</string>
<string name="rename_playlist">Aldatu izena erreprodukzio-zerrendari</string>
<string name="playlist_name_input">Izena</string>
<string name="append_playlist">Gehitu erreprodukzio-zerrendara</string>
<string name="set_as_playlist_thumbnail">Ezarri erreprodukzio-zerrendaren iruditxo gisa</string>
<string name="bookmark_playlist">Gogoko erreprodukzio-zerrenda</string>
<string name="unbookmark_playlist">Kendu gogokoa</string>
<string name="delete_playlist_prompt">Erreprodukzio zerrenda hau ezabatu nahi duzu?</string>
<string name="playlist_creation_success">Erreprodukzio-zerrenda sortuta</string>
<string name="playlist_add_stream_success">Erreprodukzio-zerrendara gehituta</string>
<string name="playlist_thumbnail_change_success">Erreprodukzio zerrendaren iruditxoa aldatuta</string>
<string name="playlist_delete_failure">Ezin izan da erreprodukzio-zerrenda ezabatu</string>
<string name="caption_none">Azpititulurik ez</string>
<string name="resize_fit">Doitu</string>
<string name="resize_fill">Bete</string>
<string name="resize_zoom">Zoom</string>
<string name="caption_auto_generated">Automatikoki sortuak</string>
<string name="caption_setting_title">Azpitituluak</string>
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko.</string>
<string name="enable_leak_canary_title">Gaitu LeakCanary</string>
<string name="enable_leak_canary_summary">Memoria galeren monitorizazioa. Aplikazioak agian ez du erantzungo memoriaren aitortza egin bitartean</string>
<string name="enable_disposed_exceptions_title">Eman bizitza-ziklo kanpoko erroreen berri</string>
<string name="import_export_title">Inportatu/Esportatu</string>
<string name="import_title">Inportatu</string>
<string name="import_from">Inportatu hemendik</string>
<string name="export_to">Esportatu hona</string>
<string name="import_ongoing">Inportatzen…</string>
<string name="export_ongoing">Esportatzen…</string>
<string name="import_file_title">Inportatu fitxategia</string>
<string name="previous_export">Aurreko esportazioa</string>
<string name="subscriptions_import_unsuccessful">Ezin izan dira harpidetzak inportatu</string>
<string name="subscriptions_export_unsuccessful">Ezin izan dira harpidetzak esportatu</string>
<string name="import_youtube_instructions">Inporttu YouTube harpidetzak esportazio fitxategia deskargatuz:
\n
\n1. Joan URL honetara: %1$s
\n2. Hasi saioa eskatzen zaizunean
\n3. Esportazio fitxategiaren deskarga hasi beharko litzateke</string>
<string name="import_soundcloud_instructions">Inportatu SoundCloud profila URL-a edo zure ID-a idatziz:
\n
\n1. Gaitu \"mahaigain modua\" web nabigatzailean (gunea ez dabil mugikorretan)
\n2. Joan URL honetara: %1$s
\n3. Hasi saioa eskatzen zaizunean
\n4. Kopiatu profilaren URL-a eraman zaizun orritik.</string>
<string name="import_soundcloud_instructions_hint">zureID,soundcloud.com/zureid</string>
<string name="import_network_expensive_warning">Eragiketa honek sarearen erabilera handia egin lezake.
\n
\nJarraitu nahi duzu?</string>
<string name="playback_speed_control">Erreprodukzio-abiaduraren kontrolak</string>
<string name="playback_tempo">Tempoa</string>
<string name="playback_pitch">Tonua</string>
<string name="unhook_checkbox">Deslotu (distortsioa sor lezake)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Lehenetsia</string>
<string name="import_settings">Ezarpenak ere inportatu nahi dituzu?</string>
<string name="use_inexact_seek_summary">Bilaketa ez zehatzak posizioak azkarrago baina prezisio gutxiagoz bilatzea ahalbidetzen du</string>
<string name="download_thumbnail_summary">"Desgaitu iruditxoak kargatzea gelditzeko eta datuak eta memoria aurrezteko. Hau aldatzean memoria eta diskoko irudien cache-ak garbituko dira."</string>
<string name="app_license">NewPipe Software Librea eta Copyleft da: Nahi eran erabili, ikertu, partekatu eta hobetu dezakezu. Zehazki, elkarbanatzea eta aldatzea Free Software Foundation-ek argitaratutako GNU General Public License-ren 3. bertsioa edo berriagoren baten terminoen arabera egiteko baimena duzu.</string>
<string name="enable_disposed_exceptions_summary">Behartu aktibitatearen bizitza ziklotik kanpo baztertu eta gero entregatu ezin diren Rx salbuespenen inguruko txostena</string>
<string name="privacy_policy_title">NewPipe pribatutasun politika</string>
<string name="privacy_policy_encouragement">NewPipe proiektuak aintzat hartzen du zure pribatutasuna. Aplikazioak ez du zure baimenik gabe daturik jasotzen.
\nNewPipe pribatutasun politikak azaltzen du zehazki bidali eta gordetako informazioa zein den kraskatze txosten bat bidaltzen duzunean.</string>
<string name="read_privacy_policy">Irakurri pribatutasun politika</string>
<string name="start_accept_privacy_policy">"European General Data Protection Regulation (GDPR) legea betetzeko, NewPipe pribatutasun politika irakurtzera gonbidaatzen zaitugu.
\nAkats txosten bat bidali ahal izateko onartu behar duzu."</string>
<string name="accept">Onartu</string>
<string name="decline">Ukatu</string>
<string name="limit_data_usage_none_description">Mugagabea</string>
<string name="limit_mobile_data_usage_title">Mugatu bereizmena datu mugikorrak erabiltzean</string>
<string name="skip_silence_checkbox">Aurreratu azkar isilunea dagoenean</string>
<string name="playback_step">Urratsa</string>
<string name="playback_reset">Leheneratu</string>
<string name="minimize_on_exit_title">Minimizatu aplikazioa aldatzean</string>
<string name="minimize_on_exit_summary">Ekintza bideo erreproduzigailu nagusitik beste aplikazio batera aldatzean — %s</string>
<string name="minimize_on_exit_none_description">Bat ere ez</string>
<string name="minimize_on_exit_background_description">Minimizatu bigarren planoko erreproduzigailura</string>
<string name="minimize_on_exit_popup_description">Minimizatu laster-liho erreproduzigailura</string>
<string name="channels">Kanalak</string>
<string name="playlists">Erreprodukzio-zerrendak</string>
<string name="tracks">Pistak</string>
<string name="users">Erabiltzaileak</string>
</resources>

View File

@@ -28,7 +28,7 @@
<string name="m4a_description">M4A  meilleure qualité</string>
<string name="download_dialog_title">Télécharger</string>
<string name="next_video_title">Vidéo suivante</string>
<string name="show_next_and_similar_title">Afficher vidéos suivantes et liées</string>
<string name="show_next_and_similar_title">Afficher les vidéos \"suivantes\" et \"similaires\"</string>
<string name="url_not_supported_toast">Lien non pris en charge</string>
<string name="settings_category_video_audio_title">Vidéo et audio</string>
<string name="settings_category_other_title">Autre</string>
@@ -70,11 +70,11 @@
<string name="duration_live">Direct</string>
<string name="could_not_load_thumbnails">Impossible de charger toutes les miniatures</string>
<string name="youtube_signature_decryption_error">Impossible de déchiffrer le lien de la vidéo</string>
<string name="youtube_signature_decryption_error">Impossible de déchiffrer la signature URL de la vidéo</string>
<string name="light_parsing_error">Impossible d\'analyser complètement le site web</string>
<string name="live_streams_not_supported">Il s\'agit d\'un direct, non supporté pour le moment.</string>
<string name="sorry_string">Désolé, une erreur inattendue s\'est produite.</string>
<string name="autoplay_by_calling_app_summary">Lire automatiquement la vidéo lorsque NewPipe est appelée par une autre application</string>
<string name="autoplay_by_calling_app_summary">Lire la vidéo lorsque NewPipe est appelée par une autre application</string>
<string name="error_report_button_text">Signaler l\'erreur par e-mail</string>
<string name="what_device_headline">Information :</string>
<string name="what_happened_headline">Ce qui s\'est passé :</string>
@@ -155,7 +155,7 @@
<string name="default_popup_resolution_title">Résolution de la fenêtre par défaut</string>
<string name="show_higher_resolutions_title">Afficher résolutions plus élevées</string>
<string name="show_higher_resolutions_summary">Certains appareils uniquement supportent la lecture 2K/4K</string>
<string name="show_higher_resolutions_summary">Seulement certains appareils supportent la lecture 2K/4K</string>
<string name="default_video_format_title">Format vidéo par défaut</string>
<string name="popup_remember_size_pos_title">Mémoriser la taille et la position de la fenêtre</string>
<string name="popup_remember_size_pos_summary">Mémoriser la dernière taille et position de la fenêtre</string>
@@ -193,7 +193,7 @@
<string name="tab_about">À propos</string>
<string name="tab_contributors">Contributeurs</string>
<string name="tab_licenses">Licences</string>
<string name="app_description">Une interface YouTube libre et légère pour Android.</string>
<string name="app_description">Lecteur de flux libre et léger pour Android.</string>
<string name="view_on_github">Voir sur GitHub</string>
<string name="app_license_title">Licence de NewPipe</string>
<string name="read_full_license">Lire la licence</string>
@@ -213,7 +213,7 @@
<string name="enable_search_history_title">Historique de recherche</string>
<string name="enable_search_history_summary">Conserver les recherches sur l\'appareil</string>
<string name="enable_watch_history_title">Historique</string>
<string name="enable_watch_history_title">Historique et cache</string>
<string name="title_activity_history">Historique</string>
<string name="title_history_search">Recherché</string>
<string name="title_history_view">Regardé</string>
@@ -271,7 +271,7 @@
<string name="popup_playing_append">En file d\'attente sur le lecteur en fenêtré</string>
<string name="play_all">Tout lire</string>
<string name="player_stream_failure">Échec de la lecture de ce flux</string>
<string name="player_stream_failure">Impossible de jouer ce flux</string>
<string name="player_unrecoverable_failure">Une erreur irrécupérable du lecteur s\'est produite</string>
<string name="no_channel_subscribed_yet">Encore aucune chaîne souscrite</string>
<string name="title_activity_background_player">Lecteur en arrière-plan</string>
@@ -279,7 +279,7 @@
<string name="play_queue_remove">Retirer</string>
<string name="play_queue_stream_detail">Détails</string>
<string name="play_queue_audio_settings">Paramètres audio</string>
<string name="show_hold_to_append_title">Afficher les fenêtres d\'aide</string>
<string name="show_hold_to_append_title">Afficher l\'aide « maintenir pour ajouter »</string>
<string name="show_hold_to_append_summary">Afficher l\'aide \"Appui long pour mettre en file d\'attente\" en appuyant sur les boutons \"Arrière-plan\" et \"Fenêtre\" sur la page de détails d\'une vidéo</string>
<string name="unknown_content">[Inconnu]</string>
@@ -296,9 +296,9 @@
<string name="start_here_on_background">Démarrer ici en arrière-plan</string>
<string name="start_here_on_popup">Démarrer ici en fenêtré</string>
<string name="donation_title">Donner</string>
<string name="donation_encouragement">NewPipe est développé par des volontaires sur leur temps libre afin de vous proposer la meilleur expérience. Maintenant il est temps de leurs offrir un café pour les soutenir dans leurs efforts et rendre NewPipe encore meilleur !</string>
<string name="donation_encouragement">NewPipe est développé par des volontaires sur leur temps libre afin de vous proposer la meilleure expérience possible. Vous pouvez leur offrir un café pour les soutenir dans leurs efforts et rendre NewPipe encore meilleur.</string>
<string name="website_title">Site</string>
<string name="website_encouragement">Pour obtenir plus d\'informations et les dernières nouvelles à propos de NewPipe, visitez notre site Internet.</string>
<string name="website_encouragement">Visitez le site internet de NewPipe pour plus d\'informations et de nouvelles.</string>
<string name="give_back">Donner en retour</string>
<string name="default_content_country_title">Pays du contenu par défaut</string>
<string name="toggle_orientation">Rotation</string>
@@ -324,7 +324,7 @@
<string name="always_ask_player">Toujours demander</string>
<string name="preferred_player_fetcher_notification_title">Obtention des infos…</string>
<string name="preferred_player_fetcher_notification_message">Le contenu demandé est en chargement</string>
<string name="preferred_player_fetcher_notification_message">Chargement du contenu</string>
<string name="import_data_title">Importer les données</string>
<string name="export_data_title">Exporter les données</string>
<string name="import_data_summary">Cela effacera vos données actuelles</string>
@@ -366,12 +366,12 @@
<string name="unbookmark_playlist">Retirer la marque</string>
<string name="delete_playlist_prompt">Voulez-vous supprimer cette playlist ?</string>
<string name="playlist_creation_success">Playlist créée avec succès</string>
<string name="playlist_creation_success">Liste de lecture créée</string>
<string name="playlist_add_stream_success">Ajoutée à la playlist</string>
<string name="playlist_thumbnail_change_success">Modification de la playlist réussie</string>
<string name="playlist_delete_failure">Échec de la suppression de la playlist</string>
<string name="playlist_delete_failure">Impossible de supprimer la liste de lecture</string>
<string name="caption_none">Aucun sous-titre</string>
<string name="caption_none">Aucuns sous-titres</string>
<string name="resize_fit">Ajuster</string>
<string name="resize_zoom">Zoom</string>
@@ -398,19 +398,19 @@
<string name="error_occurred_detail">Une erreur s\'est produite: %1$s</string>
<string name="delete_one">Supprimer un seul média</string>
<string name="drawer_header_action_paceholder_text">En cours de développement ;D</string>
<string name="drawer_header_action_paceholder_text">Quelque chose va bien bientôt arriver ;D</string>
<string name="controls_download_desc">Télécharger le fichier de flux</string>
<string name="auto_queue_title">Vidéo suivante en file d\'attente</string>
<string name="auto_queue_summary">Mettre automatiquement en file d\'attente la vidéo suivante liée à la vidéo en cours de lecture dans une file de lecture non répétitive</string>
<string name="auto_queue_summary">Ajout automatique d\'un flux connexe lors de la lecture du dernier flux dans une file d\'attente non répétitive.</string>
<string name="settings_category_debug_title">Débogage</string>
<string name="resize_fill">Remplir</string>
<string name="caption_auto_generated">Générés automatiquement</string>
<string name="enable_leak_canary_title">Activer LeakCanary</string>
<string name="enable_leak_canary_summary">Surveiller la baisse de mémoire. L\'application pourrait ne plus répondre correctement</string>
<string name="enable_leak_canary_summary">La surveillance de la mémoire peut mettre temporairement l\'application en pause pendant les nettoyages</string>
<string name="enable_disposed_exceptions_title">Signaler erreurs Out-of-lifecycle</string>
<string name="enable_disposed_exceptions_title">Signaler les erreurs de développement hors cycle</string>
<string name="enable_disposed_exceptions_summary">Forcer le signalement des exceptions Rx qui surviennent hors activité</string>
<string name="import_export_title">Importer/Exporter</string>
@@ -424,42 +424,65 @@
<string name="import_file_title">Importer fichier</string>
<string name="previous_export">Export précédent</string>
<string name="subscriptions_import_unsuccessful">Import des abonnements échoué</string>
<string name="subscriptions_export_unsuccessful">Export des abonnements échoué</string>
<string name="subscriptions_import_unsuccessful">Impossible d\'importer des abonnements</string>
<string name="subscriptions_export_unsuccessful">Impossible d\'exporter les abonnements</string>
<string name="import_youtube_instructions">\"Pour importer vos abonnements YouTube vous devez d\'abord télécharger un fichier export de YouTube, selon les modalités suivantes:
\n
\n1. Allez à ce lien: %1$s
\n2. Connectez-vous à votre compte
\n3. Le téléchargement devrait démarrer (votre fichier export YouTube)\"</string>
<string name="import_soundcloud_instructions">Pour importer vos abonnements SoundCloud vous devez connaitre l\'URL de votre profil ou votre identifiant (id). Si vous le savez, tapez-le ci-dessous.
\n
\nSi vous ne le connaissez pas, veuillez suivre les étapes suivantes:
\n
\n1. Activer le \\\"mode bureau\\\" dans votre navigateur (le site n\'est pas accesible en mode mobile)
\n2. Aller à ce lien: %1$s
<string name="import_youtube_instructions">"Pour importer vos abonnements YouTube vous devez d\'abord télécharger un fichier spécial de YouTube, selon les modalités suivantes :
\n
\n1. Suivez ce lien : %1$s
\n2. Connectez-vous à votre compte lorsque ce sera demandé
\n3. Un téléchargement va démarrer (ce sera celui du fichier nécessaire à l\'importation des abonnements)"</string>
<string name="import_soundcloud_instructions">Pour importer vos abonnements SoundCloud, vous devez connaitre l\'URL de votre profil ou votre identifiant (id). Si vous le savez, tapez-le ci-dessous.
\n
\nSi vous ne le connaissez pas, veuillez suivre les étapes suivantes :
\n
\n1. Activer le \"mode bureau\" dans votre navigateur (le site n\'est pas accesible en mode mobile)
\n2. Suivez ce : %1$s
\n3. Connectez-vous à votre compte
\n4. Copier l\'URL vers lequel vous venez d\'être redirigé (qui est l\'URL de votre profil)</string>
<string name="import_soundcloud_instructions_hint">votreid, soundcloud.com/votreid</string>
<string name="import_soundcloud_instructions_hint">votreID, soundcloud.com/votreid</string>
<string name="import_network_expensive_warning">N\'oubliez pas que cette opération peut consommer beaucoup de données mobiles.
\n
<string name="import_network_expensive_warning">Cette opération peut consommer beaucoup de données mobiles.
\n
\nSouhaitez-vous continuer ?</string>
<string name="playback_speed_control">Vitesse de lecture</string>
<string name="playback_tempo">Cadence</string>
<string name="unhook_checkbox">Unhook (déformations possibles)</string>
<string name="playback_default">Défaut</string>
<string name="preferred_open_action_settings_title">Ouverture préférée</string>
<string name="preferred_open_action_settings_title">Ouvrir de préférence avec</string>
<string name="preferred_open_action_settings_summary">Action par défaut lors de l\'ouverture de contenu - %s</string>
<string name="no_streams_available_download">Aucun flux disponible au téléchargement</string>
<string name="caption_setting_title">Sous-titre</string>
<string name="caption_setting_title">Sous-titres</string>
<string name="caption_setting_description">Modifier la taille du texte et les styles d\'arrière-plan du lecteur. Redémarrage requis pour prendre effet.</string>
<string name="playback_pitch">Ton</string>
<string name="playback_nightcore">Nightcore</string>
<string name="toast_no_player">Aucun lecteur n\'a été trouvé pour ce fichier</string>
<string name="toast_no_player">Aucune application installée pour lire ce fichier</string>
<string name="clear_views_history_title">Effacer l\'historique</string>
<string name="clear_views_history_summary">Supprimer l\'historique des flux regardés.</string>
<string name="delete_view_history_alert">Supprimer tout l\'historique regardé.</string>
<string name="view_history_deleted">Voir lhistorique supprimé.</string>
<string name="clear_search_history_title">Supprimer l\'historique des recherches</string>
<string name="clear_search_history_summary">Supprimer l\'historique de recherche par mot clef.</string>
<string name="delete_search_history_alert">Supprimer tout l\'historique de recherche.</string>
<string name="search_history_deleted">Historique des recherches effacé.</string>
<string name="one_item_deleted">1 élément supprimé.</string>
<string name="app_license">"NewPipe est un logiciel sous licence libre : Vous pouvez l\'utiliser, l\'étudier, le partager et l\'améliorer comme bon vous semble. Vous pouvez le redistribuer et/ou le modifier sous les termes de la licence générale publique GNU, comme publiée par la Free Software Foundation, dans sa version 3, ou, à votre convenance, dans une version plus récente."</string>
<string name="privacy_policy_title">Politique de confidentialité de NewPipe</string>
<string name="read_privacy_policy">Lire la politique de confidentialité</string>
<string name="import_settings">Voulez-vous également importer des paramètres ?</string>
<string name="accept">Accepter</string>
<string name="decline">Refuser</string>
<string name="privacy_policy_encouragement">Le projet NewPipe prend votre vie privée très à cœur. Ainsi, l\'appli n\'envoie aucune donnée sans votre consentement.
\nLa politique de vie privée de NewPipe explique en détail quelle donnée est envoyée et stockée quand vous envoyez un rapport de plantage.</string>
<string name="start_accept_privacy_policy">Afin de se conformer au Règlement Général sur la Protection des Données (RGPD ou GDPR), nous attirons votre attention sur la politique de vie privée de NewPipe. Merci de la lire attentivement.
\nVous devez l\'accepter pour nous envoyer le rapport de bug.</string>
<string name="limit_data_usage_none_description">Pas de limite</string>
<string name="limit_mobile_data_usage_title">Limiter la résolution en données mobile</string>
</resources>

View File

@@ -32,7 +32,7 @@
<string name="download_path_audio_dialog_title">הכנס נתיב לשמירת קבצי שמע</string>
<string name="autoplay_by_calling_app_title">נגן אוטומטית</string>
<string name="autoplay_by_calling_app_summary">הפעל סרטון אוטומטית כאשר NewPipe נפתח דרך אפליקציה אחרת</string>
<string name="autoplay_by_calling_app_summary">מנגן סרטון כאשר NewPipe נפתח דרך אפליקציה אחרת</string>
<string name="default_resolution_title">רזולוציית ברירת המחדל</string>
<string name="default_popup_resolution_title">רזולוציית ברירת המחדל לחלון צץ</string>
<string name="show_higher_resolutions_title">הצג רזולוציות גבוהות יותר</string>
@@ -119,7 +119,7 @@
<string name="enable_search_history_title">היסטוריית חיפושים</string>
<string name="enable_search_history_summary">שמור חיפושים מקומית</string>
<string name="enable_watch_history_title">היסטוריה</string>
<string name="enable_watch_history_title">היסטוריה ומטמון</string>
<string name="enable_watch_history_summary">המשך לעקוב אחר סרטונים שנצפו</string>
<string name="resume_on_audio_focus_gain_title">המשך לנגן בעת חזרת המיקוד ליישום</string>
<string name="resume_on_audio_focus_gain_summary">המשך לנגן לאחר הפרעות (לדוגמה: שיחות טלפון)</string>
@@ -386,4 +386,16 @@
\n3. ההורדה אמורה להתחיל (זהו קובץ היצוא)</string>
<string name="playback_tempo">קצב</string>
<string name="playback_default">ברירת מחדל</string>
</resources>
<string name="use_inexact_seek_title">השתמש בחיפוש מהיר שאינו מדויק</string>
<string name="use_inexact_seek_summary">חיפוש לא מדויק מאפשר לנגן לחפש נקודת זמן מהר יותר, עם דיוק מופחת</string>
<string name="download_thumbnail_title">טען תמונות ממוזערות</string>
<string name="download_thumbnail_summary">"השבת כדי לעצור את טעינת כל התמונות הממוזערות וחסוך בשימוש בנתונים ובזכרון. השינוי ימחק את המטמון בזכרון ובדיסק. "</string>
<string name="metadata_cache_wipe_summary">הסר את כל נתוני העמודים במטמון</string>
<string name="auto_queue_title">אוטומטית הכנס לתור את ההזרמה הבאה</string>
<string name="auto_queue_summary">"אוטומטית הוסף הזרמה קשורה כאשר ההזרמה האחרונה לא נמצאת במצב הזרמה חוזרת של התור. "</string>
<string name="toggle_orientation">כפתור כיוון</string>
<string name="switch_to_main">החלף לראשי</string>
<string name="import_data_summary">פעולה זו תדרוס את ההיסטוריה ורשימת המנויים הקיימת.</string>
<string name="clear_views_history_summary">"מחק הסטוריה של ניגונים קודמים. "</string>
</resources>

View File

@@ -76,7 +76,7 @@
<string name="download_path_audio_summary">डाउनलोड किये गए ऑडियो फाइल की जगह</string>
<string name="download_path_audio_dialog_title">ऑडियो फाइल डाउनलोड करने के लिए जगह दर्ज करें</string>
<string name="autoplay_by_calling_app_summary">जब NewPipe को दुसरे एप्प से बुलावा आये तोिडियो अपनी स्वेच्छा से चले</string>
<string name="autoplay_by_calling_app_summary">अन्य अप्प के द्वारा NewPipe के आह्वान परडियो तुरंत चले</string>
<string name="default_resolution_title">वीडियो का डिफ़ॉल्ट रिज़ॉल्यूशन</string>
<string name="default_popup_resolution_title">विडियो पॉपअप का डिफ़ॉल्ट रिज़ॉल्यूशन</string>
<string name="show_higher_resolutions_title">उच्च रिज़ॉल्यूशन दिखाएं</string>
@@ -93,8 +93,8 @@
<string name="dark_theme_title">काला</string>
<string name="popup_remember_size_pos_title">विडियो पॉपअप की आकर और उसकी स्थति को याद रखे</string>
<string name="popup_remember_size_pos_summary">विडियो पॉपअप के पहले वाली आकर और उसकी स्थिति को याद रखे</string>
<string name="player_gesture_controls_title">विडियो प्लेयर के gesture कण्ट्रोल</string>
<string name="player_gesture_controls_summary">विडियो प्लेयर की ब्राइटनेस और ध्वनी को नियंत्रण के लिए फ़ोन में gesture से इशारो का प्रयोग करे</string>
<string name="player_gesture_controls_title">प्लेयर इशारा नियंत्रण</string>
<string name="player_gesture_controls_summary">विडियो प्लेयर की ब्राइटनेस और ध्वनी को नियंत्रण के लिए फ़ोन में इशारो का प्रयोग करे</string>
<string name="show_search_suggestions_title">खोज के सुझाव देखे</string>
<string name="show_search_suggestions_summary">जब कुछ ढूंड रहे हो तो सुझाव दिखाये</string>
<string name="enable_search_history_title">खोज के इतिहास को देखे</string>
@@ -376,4 +376,16 @@
<string name="enable_leak_canary_summary">हीप डंप करने के दौरान मेमोरी लीक मॉनिटरिंग app को अनुत्तरदायी बना सकता है</string>
<string name="enable_disposed_exceptions_title">Out-of-Lifecycle त्रुटियों की रिपोर्ट करें</string>
<string name="download_thumbnail_title">छायाप्रारुप लोड करें</string>
<string name="use_inexact_seek_title">तेजी से अचूक तलाश का प्रयोग करें</string>
<string name="use_inexact_seek_summary">अचूक खोज प्लेयर को कम परिशुद्धता के साथ तेजी से पदों की तलाश करने की अनुमति देता है</string>
<string name="download_thumbnail_summary">सभी थंबनेल को डेटा और मेमोरी उपयोग पर लोड करने और सहेजने से रोकने के लिए अक्षम करें। इसे बदलने से इन-मेमोरी और ऑन-डिस्क छवि कैश दोनों साफ़ हो जाएंगे।</string>
<string name="thumbnail_cache_wipe_complete_notice">छवि कैश मिटा दिया</string>
<string name="metadata_cache_wipe_title">कैश मेटाडेटा वाइप करें</string>
<string name="metadata_cache_wipe_summary">सभी कैश किए गए वेबपृष्ठ डेटा हटाएं</string>
<string name="metadata_cache_wipe_complete_notice">मेटाडाटा कैश मिटा दिया गया</string>
<string name="auto_queue_title">अगली स्ट्रीम को स्वचालित रूप से जोड़ें</string>
<string name="auto_queue_summary">गैर-दोहराने वाली कतार में अंतिम स्ट्रीम चलाते समय संबंधित स्ट्रीम को स्वतः संलग्न करें।</string>
<string name="file">फाइल</string>
</resources>

View File

@@ -24,12 +24,12 @@
<string name="download_path_audio_dialog_title">Masukkan lokasi unduhan berkas audio</string>
<string name="autoplay_by_calling_app_title">Putar otomatis</string>
<string name="autoplay_by_calling_app_summary">Otomatis memutar video ketika NewPipe dijalankan dari aplikasi lain</string>
<string name="autoplay_by_calling_app_summary">Putar video ketika NewPipe dijalankan dari aplikasi lain</string>
<string name="default_resolution_title">Resolusi baku</string>
<string name="play_with_kodi_title">Putar dengan Kodi</string>
<string name="kore_not_found">Aplikasi Kore tidak ditemukan. Pasang Kore?</string>
<string name="show_play_with_kodi_title">Tampilkan opsi \"Putar dengan Kodi\"</string>
<string name="show_play_with_kodi_summary">Menampilkan opsi untuk memutar video menggunakan Kodi media center</string>
<string name="show_play_with_kodi_summary">Tampilkan opsi untuk memutar video via media center Kodi</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Format audio baku</string>
<string name="webm_description">WebM — format bebas</string>
@@ -40,9 +40,9 @@
<string name="download_dialog_title">Unduh</string>
<string name="next_video_title">Video berikutnya</string>
<string name="show_next_and_similar_title">Tampilkan video berikutnya dan terkait</string>
<string name="show_next_and_similar_title">Tampilkan video \'berikutnya\' dan \'serupa\'</string>
<string name="url_not_supported_toast">URL tidak didukung</string>
<string name="search_language_title">Bahasa konten bawaan</string>
<string name="search_language_title">Bahasa konten baku</string>
<string name="settings_category_video_audio_title">Video &amp; Audio</string>
<string name="settings_category_appearance_title">Tampilan</string>
<string name="settings_category_other_title">Lainnya</string>
@@ -50,10 +50,10 @@
<string name="play_btn_text">Putar</string>
<string name="content">Konten</string>
<string name="show_age_restricted_content_title">Tampilkan konten batasan usia</string>
<string name="video_is_age_restricted">Video Dibatasi Usia. Aktifkan video batasan usia bisa lewat Pengaturan.</string>
<string name="video_is_age_restricted">"Video Dibatasi Usia. Memperbolehkan ditonton dapat diatur lewat Pengaturan."</string>
<string name="network_error">Galat jaringan</string>
<string name="could_not_load_thumbnails">Tidak bisa memuat semua Thumbnail</string>
<string name="did_you_mean">Mungkin maksud Anda: %1$s ?</string>
<string name="did_you_mean">Maksud Anda: %1$s ?</string>
<string name="screen_rotation">rotasi</string>
<string name="duration_live">langsung</string>
<string name="downloads">Unduhan</string>
@@ -66,12 +66,12 @@
<string name="content_not_available">Konten tidak tersedia</string>
<string name="blocked_by_gema">Diblokir oleh GEMA</string>
<string name="could_not_setup_download_menu">Tidak bisa menyiapkan menu unduhan</string>
<string name="live_streams_not_supported">Ini adalah SIARAN LANGSUNG, yang mana ini belum didukung.</string>
<string name="live_streams_not_supported">Ini adalah SIARAN LANGSUNG, yang sekarang ini belum didukung.</string>
<string name="could_not_load_image">Tidak bisa memuat gambar</string>
<string name="sorry_string">Maaf, hal tersebut seharusnya tidak terjadi.</string>
<string name="error_report_button_text">Lapor galat via surat elektronik</string>
<string name="error_snackbar_message">Maaf, telah terjadi galat.</string>
<string name="error_snackbar_action">LAPOR</string>
<string name="error_snackbar_action">LAPORKAN</string>
<string name="what_device_headline">Info:</string>
<string name="what_happened_headline">Yang terjadi:</string>
<string name="your_comment">Komentar Anda (dalam bahasa Inggris):</string>
@@ -114,7 +114,7 @@
<string name="msg_copied">Disalin ke papan klip</string>
<string name="no_available_dir">Silakan pilih direktori unduhan yang tersedia</string>
<string name="no_player_found">Tidak ditemukan pemutar stream. Apakah anda ingin memasang VLC?</string>
<string name="no_player_found">Tidak ditemukan pemutar stream. Apakah Anda ingin memasang VLC?</string>
<string name="youtube_signature_decryption_error">Tidak bisa mendekrip tanda tangan URL video</string>
<string name="app_ui_crash">App/UI rusak</string>
<string name="could_not_get_stream">Tidak bisa mendapatkan stream apapun</string>
@@ -152,9 +152,9 @@ membuka di mode popup</string>
<string name="use_old_player_summary">Versi lama pemutar Mediaframework</string>
<string name="disabled">Dinonaktifkan</string>
<string name="default_video_format_title">Pilihan format video</string>
<string name="default_video_format_title">Format video baku</string>
<string name="default_popup_resolution_title">Resolusi popup bawaan</string>
<string name="default_popup_resolution_title">Resolusi popup baku</string>
<string name="show_higher_resolutions_title">Tampilkan resolusi yang lebih tinggi</string>
<string name="show_higher_resolutions_summary">Hanya perangkat tertentu yang mendukung pemutaran video 2K/4K</string>
<string name="controls_background_title">Latar Belakang</string>
@@ -165,14 +165,14 @@ membuka di mode popup</string>
<string name="filter">Filter</string>
<string name="use_external_video_player_summary">Beberapa resolusi TIDAK akan memiliki suara ketika opsi ini diaktifkan</string>
<string name="popup_remember_size_pos_title">Ingat ukuran dan posisi sembulan</string>
<string name="popup_remember_size_pos_summary">Ingat ukuran terakhir dan pengaturan posisi popup</string>
<string name="popup_remember_size_pos_title">Ingat ukuran dan posisi popup</string>
<string name="popup_remember_size_pos_summary">Ingat ukuran dan posisi terakhir popup</string>
<string name="settings_category_popup_title">Sembulan</string>
<string name="settings_category_popup_title">Popup</string>
<string name="popup_resizing_indicator_title">Ubah ukuran</string>
<string name="player_gesture_controls_title">Kontrol gerak pemutar</string>
<string name="player_gesture_controls_summary">Gunakan gerak untuk mengontrol kecerahan dan volume pemutar</string>
<string name="player_gesture_controls_title">Kontrol isyarat gerak pemutar</string>
<string name="player_gesture_controls_summary">Gunakan isyarat gerak untuk mengontrol kecerahan dan volume pemutar</string>
<string name="show_search_suggestions_title">Saran pencarian</string>
<string name="show_search_suggestions_summary">Tampilkan saran ketika mencari</string>
@@ -196,68 +196,68 @@ membuka di mode popup</string>
<string name="tab_about">Tentang</string>
<string name="tab_contributors">Kontributor</string>
<string name="tab_licenses">Lisensi</string>
<string name="app_description">Antarmuka YouTube yang ringan dan gratis untuk Android.</string>
<string name="app_description">Aplikasi streaming gratis dan ringan untuk Android.</string>
<string name="view_on_github">Lihat di Github</string>
<string name="app_license_title">Lisensi NewPipe</string>
<string name="contribution_encouragement">Terlepas apakah Anda memiliki ide, terjemahan, perubahan desain, pembersihan kode, atau perubahan kode yang signifikan, segala bantuan akan selalu diterima. Semakin banyak akan semakin baik jadinya!</string>
<string name="contribution_encouragement">Terlepas apakah Anda memiliki ide untuk; terjemahan, perubahan desain, pembersihan kode, atau perubahan kode yang signifikan, segala bantuan akan selalu diterima. Semakin banyak akan semakin baik jadinya!</string>
<string name="read_full_license">Baca lisensi</string>
<string name="contribution_title">Kontribusi</string>
<string name="subscribe_button_title">Berlangganan</string>
<string name="subscribed_button_title">Langganan</string>
<string name="subscribed_button_title">Masih Berlangganan</string>
<string name="fragment_whats_new">Apa Yang Baru</string>
<string name="resume_on_audio_focus_gain_title">Lanjutkan saat fokus</string>
<string name="resume_on_audio_focus_gain_summary">Lanjutkan pemutaran setelah interupsi (mis. panggilan telepon)</string>
<string name="tab_main">Utama</string>
<string name="enable_search_history_title">Cari riwayat</string>
<string name="enable_search_history_title">Riwayat pencarian</string>
<string name="enable_search_history_summary">Simpan pencarian secara lokal</string>
<string name="enable_watch_history_title">Riwayat</string>
<string name="enable_watch_history_title">Riwayat &amp; Tembolok</string>
<string name="notification_channel_name">Notifikasi NewPipe</string>
<string name="title_activity_history">Riwayat</string>
<string name="history_disabled">Riwayat dinonaktifkan</string>
<string name="action_history">Riwayat</string>
<string name="history_empty">Riwayat kosong</string>
<string name="download_thumbnail_title">"Muat "</string>
<string name="download_thumbnail_summary">Matikan untuk memberhentikan semua thumbnail dari pemuatan untuk menghemat data dan penggunaan memoti. Mengubah ini akan membersihkan tembolok didalam-memori dan gambar didalam-diska.</string>
<string name="thumbnail_cache_wipe_complete_notice">"Tembolok gambar "</string>
<string name="metadata_cache_wipe_title">Bersihkan metadata tembolok</string>
<string name="metadata_cache_wipe_summary">Hapus semua data tembolok laman web</string>
<string name="metadata_cache_wipe_complete_notice">Tembolok metadata dibersihkan</string>
<string name="auto_queue_summary">Otomatis menambahkan stream yg terkait ketika playback mulai saat stream terakhir di antri memutar tidak-mengulang</string>
<string name="enable_watch_history_summary">Simpan daftar video yg telah ditonton</string>
<string name="show_hold_to_append_title">Tampilkan tahan untuk menambahkan tip</string>
<string name="show_hold_to_append_summary">Tampilkan tip ketika belakang layar atau tombol sembul ditekan pada laman rinci video</string>
<string name="default_content_country_title">Konten negara bawaan</string>
<string name="download_thumbnail_title">Muat thumbnail</string>
<string name="download_thumbnail_summary">Nonaktifkan untuk berhenti memuat semua thumbnail dan menghemat penggunaan data dan memori. Mengubah ini akan menghapus tembolok gambar pada diska dan memory.</string>
<string name="thumbnail_cache_wipe_complete_notice">Tembolok gambar dihapus</string>
<string name="metadata_cache_wipe_title">Hapus tembolok metadata</string>
<string name="metadata_cache_wipe_summary">Buang semua data tembolok laman web</string>
<string name="metadata_cache_wipe_complete_notice">Tembolok metadata dihapus</string>
<string name="auto_queue_summary">Otomatis tambahkan stream yang terkait ketika memutar stream terakhir dalam antrean tanpa perulangan.</string>
<string name="enable_watch_history_summary">Simpan daftar video yang telah ditonton</string>
<string name="show_hold_to_append_title">Tampilkan tip \"tahan untuk menambahkan\"</string>
<string name="show_hold_to_append_summary">Tampilkan tip ketika tombol latar belakang atau popup ditekan pada halaman detail video</string>
<string name="default_content_country_title">Negara konten baku</string>
<string name="service_title">Layanan</string>
<string name="settings_category_player_title">Pemutar</string>
<string name="settings_category_player_behavior_title">Kebiasaan</string>
<string name="settings_category_player_behavior_title">Perilaku</string>
<string name="settings_category_history_title">Riwayat &amp; Tembolok</string>
<string name="settings_category_debug_title">Debug</string>
<string name="background_player_append">Mengantri di belakang pemutar</string>
<string name="popup_playing_append">Mengantri di pemutar sembulan</string>
<string name="playlist">Daftar putar</string>
<string name="background_player_append">Mengantre di belakang pemutar</string>
<string name="popup_playing_append">Mengantre di popup pemutar</string>
<string name="playlist">Daftar Putar</string>
<string name="undo">Tidak jadi</string>
<string name="play_all">Putar Semua</string>
<string name="always">Selalu</string>
<string name="just_once">Hanya Sekali</string>
<string name="file">Berkas</string>
<string name="notification_channel_description">Notifikasi untuk Layar Belakang NewPipe dan Pemutar Sembulan</string>
<string name="notification_channel_description">"Notifikasi untuk Latar Belakang NewPipe dan Popup Pemutar "</string>
<string name="unknown_content">[Tidak diketahui]</string>
<string name="toggle_orientation">Ubah Orientasi</string>
<string name="switch_to_background">Kembali ke Layar Belakang</string>
<string name="switch_to_popup">Kembali ke Sembulan</string>
<string name="switch_to_popup">Kembali ke Popup</string>
<string name="switch_to_main">Kembali ke Utama</string>
<string name="import_data_title">Impor basis data</string>
<string name="export_data_title">Ekspor basis data</string>
<string name="import_data_title">Impor database</string>
<string name="export_data_title">Ekspor database</string>
<string name="import_data_summary">Akan menimpa riwayat dan langganan kamu saat ini</string>
<string name="export_data_summary">Ekspor riwayat, langganan dan daftar putar.</string>
<string name="player_stream_failure">Gagal memutar stream ini</string>
<string name="player_unrecoverable_failure">Galat yg tidak bisa dibetulkan terjadi di pemutar</string>
<string name="export_data_summary">Ekspor riwayat, daftat langganan dan daftar putar.</string>
<string name="player_stream_failure">Tidak bisa memutar stream ini</string>
<string name="player_unrecoverable_failure">Galat yg tidak bisa dipulihkan terjadi di pemutar</string>
<string name="player_recoverable_failure">Memulihkan dari galat pemutar</string>
<string name="external_player_unsupported_link_type">Playar eksternal tidak mendukung tipe tautan ini</string>
<string name="invalid_url_toast">URL tidak valid</string>
@@ -265,7 +265,7 @@ membuka di mode popup</string>
<string name="audio_streams_empty">Stream audio tidak ditemukan</string>
<string name="invalid_directory">Direktori tidak valid</string>
<string name="invalid_source">Berkas/konten sumber tidak valid</string>
<string name="invalid_file">Berkas tidak ada atau tidak memiliki izin untuk membaca atau menulisnya</string>
<string name="invalid_file">Berkas tid tersedia atau tidak memiliki izin untuk membaca atau menulisnya</string>
<string name="file_name_empty_error">Nama berkas tidak boleh kosong</string>
<string name="error_occurred_detail">Sebuah galat terjadi: %1$s</string>
@@ -295,62 +295,62 @@ membuka di mode popup</string>
<string name="rename">Ubah nama</string>
<string name="donation_title">Donasi</string>
<string name="donation_encouragement">NewPipe dikembangkan oleh relawan yg menghabiskan waktu luangnya untuk pengalaman terbaik kamu. Kini saatnya memberikan kembali untuk memastikan pengembang kita dapat membuat NewPipe lebih baik lagi sambil menikmati secangkir kopi jawa!</string>
<string name="give_back">Beri balik</string>
<string name="donation_encouragement">NewPipe dikembangkan oleh relawan yang menyisihkan waktu untuk memberi Anda pengalaman terbaik. Berikan dukungan kepada pengembang membuat NewPipe menjadi lebih baik lagi sembari menikmati secangkir kopi.</string>
<string name="give_back">Beri dukungan</string>
<string name="website_title">Situs</string>
<string name="website_encouragement">Untuk mendapatkan informasi lebih jauh dan berita terbaru tentang NewPipe kunjungi situs kami.</string>
<string name="website_encouragement">Kunjungi situs web NewPipe untuk info dan berita.</string>
<string name="title_history_search">Sudah dicari</string>
<string name="history_cleared">Riwayat terhapus</string>
<string name="item_deleted">Item terhapus</string>
<string name="delete_item_search_history">Kamu ingin menghapus item ini dari riwayat pencarian?</string>
<string name="delete_stream_history_prompt">Kamu ingin menghapus item ini dari riwayat tontonan?</string>
<string name="delete_all_history_prompt">Kamu yakin ingin menghapus semua item dari riwayat?</string>
<string name="title_last_played">Diputar Terakhir</string>
<string name="title_last_played">Terakhir Diputar</string>
<string name="title_most_played">Paling sering Diputar</string>
<string name="main_page_content">Konten laman utama</string>
<string name="blank_page_summary">Laman Kosong</string>
<string name="kiosk_page_summary">Laman Kiosk</string>
<string name="kiosk_page_summary">Laman Kios</string>
<string name="subscription_page_summary">Laman Langganan</string>
<string name="feed_page_summary">Laman Umpan</string>
<string name="channel_page_summary">Laman Kanal</string>
<string name="select_a_channel">Pilih sebuah kanal</string>
<string name="no_channel_subscribed_yet">Tidak ada kanal yg dilanggani</string>
<string name="select_a_kiosk">Pilih sebuah kiosk</string>
<string name="select_a_kiosk">Pilih sebuah kios</string>
<string name="export_complete_toast">Ekspor berhasil</string>
<string name="import_complete_toast">Impor berhasil</string>
<string name="no_valid_zip_file">Berkas ZIP tidak valid</string>
<string name="could_not_import_all_files">Perhatian: Tidak dapat mengimpor semua berkas.</string>
<string name="override_current_data">Ini akan menimpa pengaturan kamu saat ini.</string>
<string name="kiosk">Kiosk</string>
<string name="kiosk">Kios</string>
<string name="trending">Tren</string>
<string name="top_50">Top 50</string>
<string name="new_and_hot">Baru &amp; Panas</string>
<string name="title_activity_background_player">Pemutar Layar Belakang</string>
<string name="title_activity_popup_player">Pemutar Sembulan</string>
<string name="title_activity_background_player">Pemutar Latar Belakang</string>
<string name="title_activity_popup_player">Popup Pemutar</string>
<string name="play_queue_remove">Hapus</string>
<string name="play_queue_stream_detail">Rincian</string>
<string name="play_queue_audio_settings">Pengaturan Audio</string>
<string name="hold_to_append">Tahan Untuk Mengantri</string>
<string name="enqueue_on_background">Antri di Layar Belakang</string>
<string name="enqueue_on_popup">Antri di Sembulan</string>
<string name="start_here_on_main">Mulai Putar Disini</string>
<string name="start_here_on_background">Mulai Disini di Layar Belakang</string>
<string name="start_here_on_popup">Mulai Disini di Sembulan</string>
<string name="hold_to_append">Tahan Untuk Mengantre</string>
<string name="enqueue_on_background">Antre di Latar Belakang</string>
<string name="enqueue_on_popup">Antre di Popup</string>
<string name="start_here_on_main">Mulai Putar di Sini</string>
<string name="start_here_on_background">Mulai Disini di Latar Belakang</string>
<string name="start_here_on_popup">Mulai Disini dengan Popup</string>
<string name="drawer_open">Bukan Menu</string>
<string name="drawer_open">Buka Menu</string>
<string name="drawer_close">Tutup Menu</string>
<string name="drawer_header_action_paceholder_text">Sesuatu akan hadir disini nanti ;D</string>
<string name="drawer_header_action_paceholder_text">Sesuatu akan muncul di sini segera ;D</string>
<string name="video_player">Pemutar video</string>
<string name="background_player">Pemutar Layar Belakang</string>
<string name="popup_player">Pemutar sembulan</string>
<string name="background_player">Pemutar Latar Belakang</string>
<string name="popup_player">Popup pemutar</string>
<string name="always_ask_open_action">Selalu tanya</string>
<string name="preferred_player_fetcher_notification_title">Mendapatkan info…</string>
<string name="preferred_player_fetcher_notification_message">Konten yg diminta sedang dimuat</string>
<string name="preferred_player_fetcher_notification_message">Memuat konten yang diminta</string>
<string name="create_playlist">Buat Daftar Putar Baru</string>
<string name="delete_playlist">Hapus Daftar Putar</string>
@@ -363,16 +363,16 @@ membuka di mode popup</string>
<string name="unbookmark_playlist">Hapus Markah</string>
<string name="delete_playlist_prompt">Kamu ingin menghapus daftar putar ini?</string>
<string name="playlist_creation_success">Daftar putar berhasil dibuat</string>
<string name="playlist_creation_success">Daftar putar dibuat</string>
<string name="playlist_add_stream_success">Ditambahkan ke daftar putar</string>
<string name="playlist_thumbnail_change_success">Thumbnail daftar putar diubah</string>
<string name="playlist_delete_failure">Gagal untuk menghapus daftar putar</string>
<string name="playlist_delete_failure">Tidak bisa menghapus daftar putar</string>
<string name="caption_none">Tidak ada deskripsi</string>
<string name="caption_none">Tidak Ada Takarir</string>
<string name="resize_fit">PAS</string>
<string name="resize_fill">ISI</string>
<string name="resize_zoom">PERBESAR</string>
<string name="resize_fit">Pas</string>
<string name="resize_fill">Isi</string>
<string name="resize_zoom">Perbesar</string>
<string name="caption_auto_generated">Dihasilkan-otomatis</string>
<string name="caption_font_size_settings_title">Ukuran fon deskripsi</string>
@@ -381,6 +381,85 @@ membuka di mode popup</string>
<string name="larger_caption_font_size">Fon lebih besar</string>
<string name="enable_leak_canary_title">Aktifkan LeakCanary</string>
<string name="playback_nightcore"></string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Bawaan</string>
</resources>
<string name="no_player_found_toast">Tidak ada aplikasi pemutar stream (Anda bisa memasang VLC untuk memutarnya)</string>
<string name="controls_download_desc">Unduh berkas stream.</string>
<string name="subscription_change_failed">Tidak bisa mengubah langganan</string>
<string name="show_info">Tampilkan info</string>
<string name="controls_add_to_playlist_title">Tambahkan Ke</string>
<string name="clear_views_history_title">Hapus riwayat menonton</string>
<string name="clear_views_history_summary">Hapus riwayat stream yang diputar.</string>
<string name="delete_view_history_alert">Hapus seluruh riwayat menonton.</string>
<string name="view_history_deleted">Riwayat menonton dihapus.</string>
<string name="clear_search_history_title">Hapus riwayat pencarian</string>
<string name="clear_search_history_summary">Hapus riwayat dari ketikan pencarian.</string>
<string name="delete_search_history_alert">Hapus seluruh riwayat pencarian.</string>
<string name="search_history_deleted">Riwayat pencarian dihapus.</string>
<string name="no_streams_available_download">Tidak ada stream yang tersedia untuk diunduh</string>
<string name="one_item_deleted">1 item di hapus.</string>
<string name="toast_no_player">Tidak ada aplikasi terpasang untuk memutar file ini</string>
<string name="title_history_view">Sudah dilihat</string>
<string name="tab_bookmarks">Markah</string>
<string name="auto_queue_title">Antre otomatis stream berikutnya</string>
<string name="channel_unsubscribed">Berhenti langganan channel ini</string>
<string name="subscription_update_failed">Tidak dapat memperbarui langganan</string>
<string name="tab_subscriptions">Langganan</string>
<string name="use_inexact_seek_title">Gunakan pencarian cepat yang tidak beraturan</string>
<string name="use_inexact_seek_summary">Pencarian tidak beraturan memungkinkan pengguna untuk mencari posisi lebih cepat dengan ketepatan yang berkurang</string>
<string name="app_license">NewPipe adalah perangkat lunak libre copyleft: Anda dapat menggunakannya, mempelajarinya, berbagi, dan meningkatkannya sesuai keinginan Anda. Secara khusus Anda dapat mendistribusikan ulang dan/atau memodifikasinya di bawah syarat-syarat Lisensi GNU General Public License yang diterbitkan oleh Free Software Foundation, baik versi 3 dari Lisensi, atau (sesuai pilihan Anda) versi yang lebih baru.</string>
<string name="import_settings">Apa Anda ingin juga mengimpor pengaturan?</string>
<string name="preferred_open_action_settings_title">Tindakan \'buka\' yang disukai</string>
<string name="preferred_open_action_settings_summary">Aksi bawaan ketika membuka konten — %s</string>
<string name="caption_setting_title">Takarir</string>
<string name="caption_setting_description">Mengubah skala keterangan teks pemutar dan gaya latar belakang. Perlu memulai ulang aplikasi untuk melihat hasilnya.</string>
<string name="enable_leak_canary_summary">Pemantauan kebocoran memori dapat menyebabkan aplikasi menjadi tidak responsif saat terjadi dumping</string>
<string name="enable_disposed_exceptions_title">Laporkan Kesalahan di-Luar-Siklus</string>
<string name="enable_disposed_exceptions_summary">Memaksa pelaporan pengecualian Rx yang tidak dapat dikirim di luar kepingan atau siklus hidup aktivitas setelah dibuang</string>
<string name="import_export_title">Impor/Ekspor</string>
<string name="import_title">Impor</string>
<string name="import_from">Impor dari</string>
<string name="export_to">Ekspor ke</string>
<string name="import_ongoing">Mengimpor…</string>
<string name="export_ongoing">Mengekspor…</string>
<string name="import_file_title">Impor file</string>
<string name="previous_export">Ekspor sebelumnya</string>
<string name="subscriptions_import_unsuccessful">Tidak dapat mengimpor langganan</string>
<string name="subscriptions_export_unsuccessful">Tidak dapat mengekspor langganan</string>
<string name="import_youtube_instructions">Impor daftar langganan YouTube dengan mengunduh file ekspor:
\n
\n1. Pergi ke URL ini: %1$s
\n2. Masuk ketika ditanya
\n3. Unduhan akan dimulai (Itulah file ekspornya)</string>
<string name="import_soundcloud_instructions">Untuk mengimpor profil SoundCloud dengan mengetik URL atau ID Anda:
\n
\n1. Nyalakan \"mode desktop\" di web-browser (situsnya tidak tersedia untuk perangkat mobile)
\n2. Pergi ke URL ini: %1$s
\n3. Log in ketika ditanya
\n4. Salin URL profil Anda ketika dialihkan.</string>
<string name="import_soundcloud_instructions_hint">IDanda, soundcloud.com/idanda</string>
<string name="import_network_expensive_warning">Perlu diingat operasi ini bisa menyedot jaringan yang mahal.
\n
\nApakah Anda ingin melanjutkan?</string>
<string name="playback_speed_control">Kontrol Kecepatan Pemutaran</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Nada</string>
<string name="unhook_checkbox">Lepas kaitan (dapat menyebabkan distorsi)</string>
</resources>

View File

@@ -28,7 +28,7 @@
<string name="m4a_description">M4A — miglior qualità</string>
<string name="download_dialog_title">Scarica</string>
<string name="next_video_title">Prossimo video</string>
<string name="show_next_and_similar_title">Mostra prossimi video e video simili</string>
<string name="show_next_and_similar_title">Mostra \'prossimi\' video e video \'simili\'</string>
<string name="url_not_supported_toast">URL non supportato</string>
<string name="search_language_title">Lingua predefinita per i contenuti</string>
<string name="settings_category_video_audio_title">Video e Audio</string>
@@ -78,7 +78,7 @@
<string name="main_bg_subtitle">Tocca cerca per iniziare</string>
<string name="autoplay_by_calling_app_title">Riproduzione automatica</string>
<string name="autoplay_by_calling_app_summary">Riproduci automaticamente i video quando NewPipe viene aperto da un\'altra app</string>
<string name="autoplay_by_calling_app_summary">Riproduci i video quando NewPipe viene aperto da un\'altra app</string>
<string name="duration_live">in diretta</string>
<string name="light_parsing_error">Impossibile analizzare completamente il sito web</string>
@@ -186,12 +186,12 @@
<string name="action_about">Informazioni</string>
<string name="title_licenses">Licenze di terze parti</string>
<string name="copyright" formatted="true">© %1$s di %2$s protetto da licenza %3$s</string>
<string name="error_unable_to_load_license">Impossible caricare il contratto di licenza</string>
<string name="error_unable_to_load_license">Impossible caricare la licenza</string>
<string name="action_open_website">Visita il sito</string>
<string name="tab_about">Informazioni</string>
<string name="tab_contributors">Contributori</string>
<string name="tab_licenses">Licenze</string>
<string name="app_description">Un\'interfaccia libera e leggera per accedere a YouTube su Android.</string>
<string name="app_description">Interfaccia libera e leggera per lo streaming di YouTube su Android.</string>
<string name="view_on_github">Mostra su GitHub</string>
<string name="app_license_title">Licenza di NewPipe</string>
<string name="contribution_encouragement">Un aiuto è sempre il benvenuto: nuove idee e funzionalità, traduzioni, modifiche al design o cambiamenti radicali del codice rendono l\'applicazione sempre migliore!</string>
@@ -210,7 +210,7 @@
<string name="enable_search_history_title">Cronologia ricerche</string>
<string name="enable_search_history_summary">Salva le ricerche</string>
<string name="enable_watch_history_title">Cronologia</string>
<string name="enable_watch_history_title">Cronologia &amp; Cache</string>
<string name="enable_watch_history_summary">Salva la cronologia dei video visualizzati</string>
<string name="resume_on_audio_focus_gain_title">Riprendi tornando in primo piano</string>
<string name="resume_on_audio_focus_gain_summary">Continua a riprodurre dopo le interruzioni (es. chiamate)</string>
@@ -218,7 +218,7 @@
<string name="settings_category_downloads_title">Scarica</string>
<string name="settings_file_charset_title">Caratteri ammessi nei nomi dei file</string>
<string name="settings_file_replacement_character_summary">I caratteri non validi vengono sostituiti con questo</string>
<string name="settings_file_replacement_character_summary">I caratteri non validi vengono sostituiti con</string>
<string name="settings_file_replacement_character_title">Carattere sostitutivo</string>
<string name="charset_letters_and_digits">Lettere e cifre</string>
@@ -304,8 +304,8 @@
<string name="start_here_on_popup">Inizia qui nel Popup</string>
<string name="donation_title">Dona</string>
<string name="website_title">Sito web</string>
<string name="website_encouragement">Per ricevere maggiori informazioni e le ultime novità su NewPipe visita il nostro sito web.</string>
<string name="donation_encouragement">NewPipe è sviluppato da volontari che trascorrono il loro tempo libero per offrirti la migliore esperienza. È il momento di restituire il favore per assicurarsi che i nostri sviluppatori possano rendere NewPipe ancora più piacevole mentre si gustano una tazza di java!</string>
<string name="website_encouragement">Visita il sito web di NewPipe per maggiori informazioni e novità.</string>
<string name="donation_encouragement">NewPipe è sviluppato da volontari che trascorrono il loro tempo libero per offrire la migliore esperienza. Restituisci il favore per aiutare gli sviluppatori a rendere NewPipe ancora più piacevole mentre si gustano una tazza di caffè.</string>
<string name="give_back">Restituisci</string>
<string name="default_content_country_title">Paese predefinito per i contenuti</string>
<string name="toggle_orientation">Cambia orientamento</string>
@@ -325,9 +325,9 @@
<string name="video_streams_empty">Nessun flusso video trovato</string>
<string name="audio_streams_empty">Nessun flusso audio trovato</string>
<string name="video_player">Riproduttore video</string>
<string name="background_player">Riproduttore di fondo</string>
<string name="popup_player">Riproduttore a comparsa</string>
<string name="video_player">Lettore video</string>
<string name="background_player">Riproduzione in sottofondo</string>
<string name="popup_player">Riproduzione in modalità popup</string>
<string name="always_ask_player">Chiedi sempre</string>
<string name="preferred_player_fetcher_notification_title">Raccogliendo informazioni…</string>
@@ -375,7 +375,7 @@
<string name="unbookmark_playlist">Rimuovi segnalibro</string>
<string name="delete_playlist_prompt">Vuoi eliminare questa playlist?</string>
<string name="playlist_creation_success">Playlist creata con successo</string>
<string name="playlist_creation_success">Playlist creata</string>
<string name="playlist_add_stream_success">Aggiunti alla playlist</string>
<string name="playlist_thumbnail_change_success">Miniatura della playlist cambiata</string>
<string name="playlist_delete_failure">Impossibile eliminare la playlist</string>
@@ -391,20 +391,20 @@
<string name="normal_caption_font_size">Carattere normale</string>
<string name="larger_caption_font_size">Carattere più grande</string>
<string name="drawer_header_action_paceholder_text">A breve qualcosa si troverà qui ;D</string>
<string name="drawer_header_action_paceholder_text">A breve qualcosa apparirà qui ;D</string>
<string name="settings_category_debug_title">Debug</string>
<string name="caption_auto_generated">Generato automaticamente</string>
<string name="enable_leak_canary_title">Abilita LeakCanary</string>
<string name="enable_leak_canary_summary">Il monitoraggio delle perdite di memoria potrebbe causare la mancata risposta dell\'applicazione durante il dumping dell\'heap</string>
<string name="enable_disposed_exceptions_title">Segnala errori \"Out-of-lifecycle\"</string>
<string name="enable_disposed_exceptions_summary">Forza la segnalazione di eccezioni Rx non consegnabili che si verificano al di fuori del ciclo di vita dell\'attività dopo la chiusura</string>
<string name="enable_disposed_exceptions_title">Segnala Errori \"Out-of-lifecycle\"</string>
<string name="enable_disposed_exceptions_summary">Forza la segnalazione di eccezioni Rx non consegnabili al di fuori del ciclo di vita dell\'attività dopo la chiusura</string>
<string name="use_inexact_seek_title">Usa la ricerca rapida ma imprecisa</string>
<string name="use_inexact_seek_summary">La ricerca imprecisa permette al lettore multimediale di spostarsi nelle posizioni più velocemente con una precisione ridotta</string>
<string name="auto_queue_title">Metti in coda automaticamente il prossimo flusso</string>
<string name="auto_queue_summary">Aggiungi automaticamente un flusso correlato quando la riproduzione inizia dall\'ultimo flusso in una coda non ripetitiva.</string>
<string name="auto_queue_summary">Aggiungi automaticamente un flusso correlato quando è in corso la riproduzione dell\'ultimo flusso in una coda non ripetitiva.</string>
<string name="live_sync">SINCRONIZZAZIONE</string>
<string name="file">File</string>
@@ -418,7 +418,7 @@
<string name="import_export_title">Importa/Esporta</string>
<string name="import_title">Importa</string>
<string name="import_from">Importa da</string>
<string name="export_to">Esporta a</string>
<string name="export_to">Esporta in</string>
<string name="import_ongoing">Importando…</string>
<string name="export_ongoing">Esportando…</string>
@@ -426,23 +426,21 @@
<string name="import_file_title">Importa file</string>
<string name="previous_export">Esportazione precedente</string>
<string name="subscriptions_import_unsuccessful">L\'importazione delle iscrizioni è fallita</string>
<string name="subscriptions_export_unsuccessful">L\'esportazione delle iscrizioni è fallita</string>
<string name="subscriptions_import_unsuccessful">Impossibile importare le iscrizioni</string>
<string name="subscriptions_export_unsuccessful">Impossibile esportare le iscrizioni</string>
<string name="import_youtube_instructions">Per importare le tue iscrizioni di YouTube è necessario il file d\'esportazione, che può essere scaricato seguendo le seguenti istruzioni:
<string name="import_youtube_instructions">Importa le iscrizioni di YouTube scaricando il file d\'esportazione:
\n
\n1. Vai a questo URL: %1$s
\n2. Accedi al tuo account quando richiesto
\n1. Vai a questo URL: %1$s
\n2. Accedi quando richiesto
\n3. Il download del file d\'esportazione dovrebbe partire in automatico</string>
<string name="import_soundcloud_instructions">Per importare gli artisti che segui su SoundCloud devi conoscere l\'URL del tuo profilo o il tuo ID. Se ne sei a conoscenza, digita uno di questi nel campo qui sotto e sei pronto per iniziare.
\n
\nSe non lo sai, puoi seguire le seguenti istruzioni:
\n
\n1. Abilita la \"modalità desktop\" nel browser che utilizzi (il sito non è disponibile per i dispositivi mobili)
<string name="import_soundcloud_instructions">Importa un profilo SoundCloud digitando l\'URL o il tuo ID:
\n
\n1. Abilita la \"modalità desktop\" nel browser (il sito non è disponibile per i dispositivi mobili)
\n2. Vai a questo URL: %1$s
\n3. Accedi al tuo account quando richiesto
\n4. Copia l\'URL a cui vieni indirizzato (è l\'URL del tuo profilo)</string>
<string name="import_soundcloud_instructions_hint">iltuoid, soundcloud.com/iltuoid</string>
\n3. Accedi quando richiesto
\n4. Copia l\'URL del profilo a cui vieni indirizzato.</string>
<string name="import_soundcloud_instructions_hint">iltuoID, soundcloud.com/iltuoid</string>
<string name="import_network_expensive_warning">Tieni presente che questa operazione può consumare una grande quantità di traffico dati.
\n
@@ -453,30 +451,54 @@
<string name="metadata_cache_wipe_title">Pulisci la cache dei metadati</string>
<string name="metadata_cache_wipe_summary">Rimuovi tutti i dati delle pagine web memorizzati nella cache</string>
<string name="metadata_cache_wipe_complete_notice">Pulizia della cache dei metadati completata</string>
<string name="playback_speed_control">Controllo della velocità di riproduzione</string>
<string name="playback_speed_control">Controlli della velocità di riproduzione</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Tono</string>
<string name="unhook_checkbox">Slega (può causare distorsione)</string>
<string name="unhook_checkbox">Scollega (può causare distorsione)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Valore predefinito</string>
<string name="no_streams_available_download">Nessun flusso disponibile per il download</string>
<string name="preferred_open_action_settings_title">Apri preferibilmente con</string>
<string name="preferred_open_action_settings_title">\'Apri\' preferibilmente con</string>
<string name="preferred_open_action_settings_summary">Azione predefinita all\'apertura del contenuto — %s</string>
<string name="caption_setting_title">Sottotitoli</string>
<string name="caption_setting_description">Modifica la dimensione e gli stili di sfondo dei sottotitoli. Richiede il riavvio del lettore multimediale per rendere effettive le modifiche.</string>
<string name="caption_setting_description">Modifica la dimensione e gli stili di sfondo dei sottotitoli. Richiede il riavvio dell\'app per rendere effettive le modifiche.</string>
<string name="toast_no_player">Non è stato trovato nessun riproduttore per questo file</string>
<string name="toast_no_player">Nessuna app installata per riprodurre questo file</string>
<string name="clear_views_history_title">Pulisci cronologia visualizzazioni</string>
<string name="clear_views_history_summary">Elimina la cronologia dei flussi riprodotti.</string>
<string name="delete_view_history_alert">Elimina l\'intera cronologia visualizzazioni.</string>
<string name="view_history_deleted">Cronologia visualizzazioni eliminata.</string>
<string name="clear_search_history_title">Pulisci cronologia delle ricerche</string>
<string name="clear_search_history_summary">Elimina la cronologia delle ricerche effettuate.</string>
<string name="clear_search_history_summary">Cancella la cronologia dei termini di ricerca.</string>
<string name="delete_search_history_alert">Elimina l\'intera cronologia delle ricerche.</string>
<string name="search_history_deleted">Cronologia delle ricerche eliminata.</string>
<string name="one_item_deleted">1 elemento eliminato.</string>
</resources>
<string name="app_license">NewPipe è un software libero con licenza copyleft: puoi usarlo, studiarlo, condividerlo e migliorarlo a tuo piacimento. In particolare, è possibile ridistribuirlo e/o modificarlo secondo i termini della GNU General Public License pubblicata dalla Free Software Foundation, sia nella versione 3 della Licenza, sia (a propria discrezione) in qualsiasi versione successiva.</string>
<string name="import_settings">Vuoi anche importare le impostazioni?</string>
<string name="privacy_policy_title">Informativa sulla privacy</string>
<string name="privacy_policy_encouragement">Il progetto NewPipe tiene molto alla tua privacy. Perciò, l\'app non raccoglie alcun dato senza il tuo consenso.
\nL\'informativa sulla privacy di NewPipe spiega in dettaglio quali dati vengono inviati e memorizzati quando si invia un rapporto sugli arresti anomali.</string>
<string name="read_privacy_policy">Leggi l\'informativa sulla privacy</string>
<string name="start_accept_privacy_policy">Per rispettare il regolamento europeo sulla protezione dei dati (GDPR), attiriamo la vostra attenzione riguardo l\'informativa sulla privacy di NewPipe. Si prega di leggerla attentamente.
\nDevi accettarla per inviarci il bug report.</string>
<string name="accept">Accetto</string>
<string name="decline">Rifiuto</string>
<string name="limit_data_usage_none_description">Senza limiti</string>
<string name="limit_mobile_data_usage_title">Limita la risoluzione quando si utilizzano dati mobili</string>
<string name="skip_silence_checkbox">Avanzamento veloce durante il silenzio</string>
<string name="playback_step">Step</string>
<string name="playback_reset">Reset</string>
<string name="minimize_on_exit_title">Minimizza al cambio applicazione</string>
<string name="minimize_on_exit_summary">Azione quando si passa ad un\'altra applicazione dal lettore video principale — %s</string>
<string name="minimize_on_exit_none_description">Nessuna</string>
<string name="minimize_on_exit_background_description">Minimizza al lettore in sottofondo</string>
<string name="minimize_on_exit_popup_description">Minimizza al lettore popup</string>
</resources>

View File

@@ -382,4 +382,15 @@
<string name="playback_default">デフォルト</string>
<string name="background_player_append">バックグラウンド再生リストに追加されました</string>
<string name="popup_playing_append">ポップアップ再生リストに追加されました</string>
<string name="clear_views_history_title">再生履歴を消去</string>
<string name="clear_views_history_summary">再生した動画の履歴を削除します。</string>
<string name="view_history_deleted">再生履歴を削除しました。</string>
<string name="clear_search_history_title">検索履歴を消去</string>
<string name="clear_search_history_summary">検索キーワードの履歴を削除します。</string>
<string name="search_history_deleted">検索履歴を削除しました。</string>
<string name="toast_no_player">このファイルを再生するためのアプリがインストールされていません</string>
<string name="import_settings">設定をインポートしますか?</string>
<string name="caption_setting_title">字幕</string>
</resources>

View File

@@ -47,7 +47,7 @@
<string name="download_path_audio_dialog_title">Внеси локација за зачувување аудио</string>
<string name="autoplay_by_calling_app_title">Автоматско пуштање</string>
<string name="autoplay_by_calling_app_summary">Автоматски пушта видео кога NewPipe е повикана од друга апликација</string>
<string name="autoplay_by_calling_app_summary">Пушта видео кога NewPipe е повикана од друга апликација</string>
<string name="default_resolution_title">Стандардна резолуција</string>
<string name="default_popup_resolution_title">Стандардна резолуција на подпрозорчето</string>
<string name="show_higher_resolutions_title">Покажи повисоки резолуции</string>
@@ -83,13 +83,13 @@
<string name="show_search_suggestions_summary">Покажи предлози при пребарување</string>
<string name="enable_search_history_title">Историја на пребарувањата</string>
<string name="enable_search_history_summary">Зачувај ги пребарувањата локално</string>
<string name="enable_watch_history_title">Историја</string>
<string name="enable_watch_history_title">Историја и зачувано</string>
<string name="enable_watch_history_summary">Зачувај ја листата на гледани видеа</string>
<string name="resume_on_audio_focus_gain_title">Продолжи видео при враќање на фокусот</string>
<string name="resume_on_audio_focus_gain_summary">Продолжи го видеото по прекини (пр. телефонски повик)</string>
<string name="download_dialog_title">Превземи</string>
<string name="next_video_title">Следно видео</string>
<string name="show_next_and_similar_title">Покажи следни и слични видеа</string>
<string name="show_next_and_similar_title">Покажи следни и слични видеа</string>
<string name="show_hold_to_append_title">Покажи совет „задржи за прикачување“</string>
<string name="show_hold_to_append_summary">Прикажи совет кога позадината или копче од подпрозорчето се притиснати, на страната за видео детаљи</string>
<string name="url_not_supported_toast">Неподдржана URL врска</string>
@@ -106,7 +106,7 @@
<string name="settings_category_debug_title">Дебагирање</string>
<string name="background_player_playing_toast">Пуштено во позадина</string>
<string name="popup_playing_toast">Пуштено во прозорче</string>
<string name="background_player_append">Ставено на листа, за заднинско пуштање</string>
<string name="background_player_append">Ставено на листата за заднинско пуштање</string>
<string name="popup_playing_append">Ставено на листа, за пуштање во прозорче</string>
<string name="play_btn_text">Пушти</string>
<string name="content">Содржина</string>
@@ -257,7 +257,7 @@
<string name="msg_popup_permission">Оваа привилегија е потребна за
\nотворање во подпрозорче</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA Предизвик</string>
<string name="recaptcha_request_toast">Потребен е reCAPTCHA предизвик</string>
@@ -284,10 +284,10 @@
<string name="contribution_encouragement">Ако имаш идеи за; превод, дизајн, чистење или големи промени во кодот - помошта е секогаш добредојдена. Што повеќе се работи, подобра е апликацијата!</string>
<string name="view_on_github">Види на GitHub</string>
<string name="donation_title">Донирај</string>
<string name="donation_encouragement">NewPipe се гради од волонтери кои го вложуваат слободното време за важето задоволство. Време е да им возвратиме за тие да можат да ја подобрат апликацијата, уживајќи во топло кафе!</string>
<string name="donation_encouragement">NewPipe се гради од волонтери кои го вложуваат слободното време за важето задоволство. Време е да им возвратиме за тие да можат да ја подобрат апликацијата, уживајќи во топло кафе.</string>
<string name="give_back">Возврати</string>
<string name="website_title">Вебсајт</string>
<string name="website_encouragement">За повеќе информации и најновите новости за NewPipe посети ја вебстраната.</string>
<string name="website_encouragement">За повеќе информации и новости за NewPipe посети ја вебстраната.</string>
<string name="app_license_title">"Лиценцата на NewPipe "</string>
<string name="read_full_license">Прочитај лиценца</string>
@@ -361,16 +361,16 @@
<string name="unbookmark_playlist">Избриши ја белешката</string>
<string name="delete_playlist_prompt">Сакаш да ја избришеш плејлистата?</string>
<string name="playlist_creation_success">Успешно создадена плејлиста</string>
<string name="playlist_creation_success">Листата е создадена</string>
<string name="playlist_add_stream_success">Додадено во плејлиста</string>
<string name="playlist_thumbnail_change_success">Се смени иконата на плејлистата</string>
<string name="playlist_delete_failure">Неуспешно бришење на плејлистата</string>
<string name="playlist_delete_failure">Неуспешно бришење на листата</string>
<string name="caption_none">Без превод</string>
<string name="resize_fit">ВКЛОПИ</string>
<string name="resize_fill">ИСПОЛНИ</string>
<string name="resize_zoom">ЗУМИРАЈ</string>
<string name="resize_fit">Вклопи</string>
<string name="resize_fill">Исполни</string>
<string name="resize_zoom">Зумирај</string>
<string name="caption_auto_generated">Автоматски создадено</string>
<string name="caption_font_size_settings_title">Големина на преводот</string>
@@ -398,29 +398,61 @@
<string name="subscriptions_import_unsuccessful">Неуспешно внесување членства</string>
<string name="subscriptions_export_unsuccessful">Неуспешно изнесување членства</string>
<string name="import_youtube_instructions">За да ги внесеш твоите YouTube членства, ти треба фајл кој можеш да го добиеш на следниот начин:
\n
\n1. Оди на оваа врска: %1$s
\n2. Пријави се на твојот профил
<string name="import_youtube_instructions">Внеси YouTube членства, преку симнување на извозен фајл:
\n
\n1. Оди на оваа врска: %1$s
\n2. Пријави се на твојот профил
\n3. Ќе започне превземање (тој фајл ти треба)</string>
<string name="import_soundcloud_instructions">За да ги внесете вашите SoundCloud членства, ви треба URL или ID на профилот. Ако ги знаете, само внесете едно од нив во полето, и готови сте.
\n
\nАко не ги знаете, одете по следните чекори:
\n
\n1. Вклучете „десктоп режим“ во вашиот пребарувач (страната не е достапна за мобилни уреди)
\n2. Одете на оваа врска: %1$s
\n3. Пријавете се на вашиот профил
\n4. Копирајте го URL-то на кое бевте пренасочени (тоа е URL-то на вашиот профил)</string>
<string name="import_soundcloud_instructions">Внеси SoundCloud членства, преку внесување URL или ID на профилот:
\n
\n1. Вклучете „десктоп режим“ во вашиот пребарувач (страната не е достапна за мобилни уреди)
\n2. Одете на оваа врска: %1$s
\n3. Пријавете се на вашиот профил
\n4. Копирајте го URL-то на кое бевте пренасочени (тоа е URL-то на вашиот профил).</string>
<string name="import_soundcloud_instructions_hint">korisnickoime, soundcloud.com/korisnickoime</string>
<string name="import_network_expensive_warning">Треба да знаеш дека оваа операција троши многу интернет.
\n
\nСакаш да продолжиш?</string>
<string name="playback_speed_control">Контрола на брзина на траката</string>
<string name="playback_speed_control">Контрола на брзината на траката</string>
<string name="playback_tempo">Темпо</string>
<string name="playback_pitch">Тон</string>
<string name="unhook_checkbox">Откачи (може да создаде мутации)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Стандардно</string>
</resources>
<string name="clear_views_history_title">Избриши историја на гледаност</string>
<string name="clear_views_history_summary">Ја брише историјата на пуштени видеа.</string>
<string name="delete_view_history_alert">Избриши ја целата историја на гледаност.</string>
<string name="view_history_deleted">Избришана е историјата на гледаност.</string>
<string name="clear_search_history_title">Избриши историја на пребарувања</string>
<string name="clear_search_history_summary">Ја брише историјата на пребарувања.</string>
<string name="delete_search_history_alert">Избриши ја целата историја на пребарувања.</string>
<string name="search_history_deleted">Избришана е историјата на пребарувања.</string>
<string name="no_streams_available_download">Нема стримови за симнување</string>
<string name="one_item_deleted">1 ставка избришана.</string>
<string name="toast_no_player">Нема апликација за пуштање на овој фајл</string>
<string name="app_license">NewPipe е „copyleft“ слободен софтвер: Можеш да ја користиш, истражуваш и подобруваш по твоја желба. Можеш да ја редистрибуираш и/или да ја промениш под условите на GNU GPL лиценцата, публикувана од фондацијата FSF - или верзија 3 од лиценцата, или (по можност) понова верзија.</string>
<string name="import_settings">Дали сакаш да се внесат и подесувањата?</string>
<string name="preferred_open_action_settings_title">Претпочитана акција за „отворање“</string>
<string name="preferred_open_action_settings_summary">Стандардна акција при отворање видеа — %s</string>
<string name="caption_setting_title">Преводи</string>
<string name="caption_setting_description">Смени ја големината и стилот на преводот. Потребен е рестарт за промена.</string>
<string name="privacy_policy_title">NewPipe - политика за приватност</string>
<string name="privacy_policy_encouragement">Проектот NewPipe сериозно ја сфаќа вашата приватност. Затоа апликацијата не собира ваши податоци без ваша дозвола.
\nПолитиката за приватност на NewPipe детално објаснува кои податоци се зачувани и пратени кога праќате извештај за грешка во апликацијата.</string>
<string name="read_privacy_policy">Прочитај ја политиката за приватност</string>
<string name="start_accept_privacy_policy">За да постапуваме соодветно со регулацијата за заштита на податоци (GDPR) на ЕУ, вараме да обрнете внимание на политиката за приватност на NewPipe.
\nВе молиме прочитајте ја внимателно. Мора да ја прифатите за да ни го испратите извештајот за грешка во апликацијата.</string>
<string name="accept">Прифати</string>
<string name="decline">Отфрли</string>
<string name="limit_data_usage_none_description">Неограничено</string>
<string name="limit_mobile_data_usage_title">Ограничи резолуција при користење мобилен интернет</string>
</resources>

View File

@@ -441,4 +441,46 @@
<string name="caption_setting_title">Undertekster</string>
<string name="caption_setting_description">Endre undertekststørrelse og bakgrunnsstiler. Krever omstart av programmet for å tre i effekt.</string>
</resources>
<string name="app_license">NewPipe er copyleft, fri programvare: Du kan bruke, studere og forbedre etter egen vilje. Spesifikt kan du redistribuere og/eller modifisere det i henhold til vilkårene gitt i GNU General Public-lisensen, som publisert av Free Software Foundation, enten versjon 3 av lisensen, eller (etter eget ønske) enhver senere versjon.</string>
<string name="import_settings">Ønsker du også å importere innstillinger?</string>
<string name="subscriptions_import_unsuccessful">Kunne ikke importere abonnementer</string>
<string name="subscriptions_export_unsuccessful">Kunne ikke eksportere abonnementer</string>
<string name="import_youtube_instructions">Importer YoutTube-abonnementer ved å laste ned eksportfilen:
\n
\n1. Gå til denne nettadressen: %1$s
\n2. Logg inn når forespurt
\n3. En nedlasting av eksportfilen bør starte</string>
<string name="import_soundcloud_instructions">Importer en SoundCloud-profil ved å skrive enten nettadressen eller din ID:
\n
\n1. Skru på \"skrivebordsmodus\" i en nettleser ( siden er ikke tilgjengelig for mobile enheter)
\n2. Logg inn når forespurt
\n4. Kopier profil-nettadressen du ble videresendt til.</string>
<string name="use_inexact_seek_summary">Unøyaktig blafring tillater spilleren å blafre til posisjoner raskere med redusert nøyaktighet</string>
<string name="download_thumbnail_summary">Skru av for å stoppe alle miniatyrbilder fra innlasting og spare data og minnebruke. Endring av dette vil tømme både disk- og minne-hurtiglager.</string>
<string name="auto_queue_summary">Legg til en relatert strøm ved avspilling av forrige strøm i ikke-repeterende kø.</string>
<string name="enable_leak_canary_summary">Minnelekkasjeoppsyn kan forårsake programmet å opptre uresponsivt under haugdumping</string>
<string name="enable_disposed_exceptions_title">Rapporter feil som opptrer utenfor sin levetid</string>
<string name="enable_disposed_exceptions_summary">Tving rapportering av uleverbare Rx-unntak utenom fragment eller aktivitetslevetid etter forkastelse</string>
<string name="unhook_checkbox">Avhekt (kan forårsake forvrenging)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="privacy_policy_title">NewPipes personvernspraksis</string>
<string name="privacy_policy_encouragement">NewPipe-prosjektet tar ditt personvern veldig alvorlig. Derfor samler ikke programmet inn data uten ditt samtykke.
\nNewPipes personvernspraksis forklarer i detalj hvilken deta som sendes og lagres når du sender en kræsjrapport.</string>
<string name="read_privacy_policy">Les personvernspraksis</string>
<string name="start_accept_privacy_policy">For å overholde Europeisk Generell Data Proteksjons-Regularing (GDPR), vil vi rette oppmerksomheten din mot NewPipes personvernspraksis. Les den nøye.
\nDu må godta den for å sende oss feilrapporten.</string>
<string name="accept">Godta</string>
<string name="decline">Avslå</string>
<string name="limit_data_usage_none_description">Ubegrenset</string>
<string name="limit_mobile_data_usage_title">Begrens oppløsning når mobildata brukes</string>
<string name="minimize_on_exit_title">Minimer ved programbytte</string>
<string name="minimize_on_exit_summary">Handling ved bytting til annet program fra hovedspiller — %s</string>
<string name="minimize_on_exit_none_description">Ingen</string>
<string name="minimize_on_exit_background_description">Minimer til bakgrunnsspiller</string>
<string name="minimize_on_exit_popup_description">Minimer til oppsprettsspiller</string>
</resources>

View File

@@ -436,4 +436,26 @@
<string name="one_item_deleted">1 item verwijderd.</string>
<string name="app_license">NewPipe is vrije software: ge kund het gebruiken, bestuderen, delen en verbeteren zoveel als da ge maar wild. Ge kund het opnieuw uitgeven en/of aanpassen volgens de voorwaarden van de GNU General Public License, gepubliceerd door de Free Software Foundation, versie 3 van de licentie, of (indien gewenst) gelijk welke latere versie.</string>
<string name="import_settings">Wild ge dinstellingen ook importeren?</string>
<string name="privacy_policy_title">Privacybeleid van NewPipe</string>
<string name="privacy_policy_encouragement">t NewPipe-project neemt uw privacy ter harte. Daarom verzameld den app geen gegevens zonder uw toestemming.
\nt Privacybeleid van NewPipe legd in detail uit welke gegevens da der worden verzonden en opgeslagen wanneer da geen crashrappor indiend.</string>
<string name="read_privacy_policy">Privacybeleid lezen</string>
<string name="start_accept_privacy_policy">Voor dEuropese privacywet (ook wel GDPR genoemd) na te leven, wijzen wu op t nieuw privacybeleid van NewPipe. Leesd t aandachtig.
\nGe moet t aanvaarden voor ons t bugrapport te sturen.</string>
<string name="accept">Aanvaarden</string>
<string name="decline">Weigeren</string>
<string name="limit_data_usage_none_description">Onbeperkt</string>
<string name="limit_mobile_data_usage_title">Resolutie beperken bij gebruik van mobiele gegevens</string>
<string name="minimize_on_exit_title">Minimaliseren bij overschakelen naar anderen app</string>
<string name="minimize_on_exit_summary">Actie bij overschakelen van videospeler naar anderen app — %s</string>
<string name="minimize_on_exit_none_description">Geen</string>
<string name="minimize_on_exit_background_description">Afspelen in achtergrond</string>
<string name="minimize_on_exit_popup_description">Afspelen in pop-up</string>
<string name="skip_silence_checkbox">Doorspoelen tijdens stilte</string>
<string name="playback_step">Stap</string>
<string name="playback_reset">Standaardwaarden herstellen</string>
</resources>

View File

@@ -449,7 +449,7 @@ te openen in pop-upmodus</string>
<string name="playback_speed_control">Afspeelsnelheidbesturing</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Toon</string>
<string name="unhook_checkbox">Ontkoppelen (kan ruis veroorzaken)</string>
<string name="unhook_checkbox">Ontlinken (kan ruis veroorzaken)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Standaard</string>
<string name="preferred_open_action_settings_title">Voorkeursactie voor openen</string>
@@ -473,4 +473,30 @@ te openen in pop-upmodus</string>
<string name="one_item_deleted">1 item verwijderd.</string>
<string name="app_license">NewPipe is vrije software: je kan het gebruiken, bestuderen, delen en verbeteren zoveel je maar wil. Je kan het opnieuw uitgeven en/of aanpassen volgens de voorwaarden van de GNU General Public License, gepubliceerd door de Free Software Foundation, versie 3 van de licentie, of (indien gewenst) om het even welke latere versie.</string>
<string name="import_settings">Wil je ook de instellingen importeren?</string>
<string name="privacy_policy_title">NewPipe\'s privacybeleid</string>
<string name="privacy_policy_encouragement">Het NewPipe-project neemt privacy serieus. Daarom verzamelt de app geen gegevens zonder jouw toestemming.
\nNewPipe\'s privacybeleid legt gedetailleerd uit welke gegevens verstuurd en opgeslagen worden als je een crashrapport verstuurd.</string>
<string name="read_privacy_policy">Privacybeleid lezen</string>
<string name="start_accept_privacy_policy">Om de Europese Algemene Verordening Gegevensbescherming (ook wel: AVG of GDPR) na te leven, wijzen we je op het nieuwe privacybeleid van NewPipe. Lees dit zorgvuldig.
\nJe moet het beleid accepteren om ons het bugrapport te kunnen sturen.</string>
<string name="accept">Accepteren</string>
<string name="decline">Weigeren</string>
<string name="limit_data_usage_none_description">Ongelimiteerd</string>
<string name="limit_mobile_data_usage_title">Resolutie beperken bij gebruik van mobiele gegevens</string>
<string name="minimize_on_exit_title">Minimaliseren bij overschakelen naar andere app</string>
<string name="minimize_on_exit_summary">Actie bij overschakelen van videospeler naar andere app — %s</string>
<string name="minimize_on_exit_none_description">Geen</string>
<string name="minimize_on_exit_background_description">Afspelen op achtergrond</string>
<string name="minimize_on_exit_popup_description">Afspelen in pop-upvenster</string>
<string name="skip_silence_checkbox">Vooruitspoelen tijdens stilte</string>
<string name="playback_step">Stap</string>
<string name="playback_reset">Standaardwaarden</string>
<string name="channels">Kanalen</string>
<string name="playlists">Afspeellijsten</string>
<string name="tracks">Nummers</string>
<string name="users">Gebruikers</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਸਰਚ ਦਬਾਓ</string>
<string name="view_count_text">%1$s ਦੇਖੇ</string>
<string name="upload_date_text">%1$s ਤੇ ਪਬਲਿਸ਼ ਕੀਤਾ</string>
<string name="no_player_found">ਸਟਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ . ਤੁਸੀ VLC ਭਰਨਾ ਚਾਹੋਗੇ?</string>
<string name="no_player_found_toast">ਸਟਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ (ਤੁਸੀਂ vlc ਇੰਸਟਾਲ ਕਰ ਸਕਦੇ ਹੋ)</string>
<string name="install">ਭਰੋ</string>
<string name="cancel">ਨਹੀਂ</string>
<string name="open_in_browser">Browser ਚ ਖੋਲੋ</string>
<string name="open_in_popup_mode">Popup ਚ ਖੋਲੋ</string>
<string name="share">ਭੇਜੋ</string>
<string name="download">ਡਾਊਨਲੋਡ</string>
<string name="controls_download_desc">ਡਾਊਨਲੋਡ ਸਟਰੀਮ ਫਾਈਲ.</string>
<string name="search">ਖੋਜੋ</string>
<string name="settings">ਸੇਟਿੰਗਾਂ</string>
<string name="did_you_mean">ਕੀ ਤੁਹਾਡਾ ਮਤਲਬ: %1$s ?</string>
<string name="share_dialog_title">ਭੇਜੋ</string>
<string name="choose_browser">Browser ਚੁਣੋ</string>
<string name="screen_rotation">ਉਲਟਾਨਾ</string>
<string name="use_external_video_player_title">ਹੋਰ ਪਲੇਅਰ ਵਰਤਣਾ</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More