Merge pull request #3741 from fynngodau/bandcamp

Bandcamp support
This commit is contained in:
Tobi 2021-03-14 20:00:40 +01:00 committed by GitHub
commit 3e83bb0d95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 185 additions and 9 deletions

View File

@ -84,6 +84,7 @@ NewPipe は複数のサービスに対応しています。[ドキュメント](
* SoundCloud \[ベータ\] * SoundCloud \[ベータ\]
* media.ccc.de \[ベータ\] * media.ccc.de \[ベータ\]
* PeerTube インスタンス \[ベータ\] * PeerTube インスタンス \[ベータ\]
* Bandcamp \[ベータ\]
<!-- Hidden span to keep old links compatible. --> <!-- Hidden span to keep old links compatible. -->
<span id="updates"></span> <span id="updates"></span>

View File

@ -79,6 +79,7 @@ NewPipe는 여러가지 서비스를 지원합니다. 우리의 [문서](https:/
* SoundCloud \[beta\] * SoundCloud \[beta\]
* media.ccc.de \[beta\] * media.ccc.de \[beta\]
* PeerTube instances \[beta\] * PeerTube instances \[beta\]
* Bandcamp \[beta\]
## Updates ## Updates
NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 인해), 결국 릴리즈가 발생할 것입니다. 이것들의 형식은 x.xx.x 입니다. NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 인해), 결국 릴리즈가 발생할 것입니다. 이것들의 형식은 x.xx.x 입니다.

View File

@ -81,6 +81,7 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc
* SoundCloud \[beta\] * SoundCloud \[beta\]
* media.ccc.de \[beta\] * media.ccc.de \[beta\]
* PeerTube instances \[beta\] * PeerTube instances \[beta\]
* Bandcamp \[beta\]
<!-- Hidden span to keep old links compatible. --> <!-- Hidden span to keep old links compatible. -->
<span id="updates"></span> <span id="updates"></span>

View File

@ -79,6 +79,7 @@ O NewPipe suporta vários serviços. Nosso [documentação](https://teamnewpipe.
* SoundCloud \[beta\] * SoundCloud \[beta\]
* media.ccc.de \[beta\] * media.ccc.de \[beta\]
* PeerTube instances \[beta\] * PeerTube instances \[beta\]
* Bandcamp \[beta\]
## Atualizações ## Atualizações
Quando uma alteração no código NewPipe (devido à adição de recursos ou fixação de bugs), eventualmente ocorrerá uma versão. Estes estão no formato x.xx.x . A fim de obter esta nova versão, você pode: Quando uma alteração no código NewPipe (devido à adição de recursos ou fixação de bugs), eventualmente ocorrerá uma versão. Estes estão no formato x.xx.x . A fim de obter esta nova versão, você pode:

View File

@ -81,6 +81,7 @@ NewPipe suportă servicii multiple. [Documentele](https://teamnewpipe.github.io/
* SoundCloud \[beta\] * SoundCloud \[beta\]
* media.ccc.de \[beta\] * media.ccc.de \[beta\]
* Instanţe PeerTube \[beta\] * Instanţe PeerTube \[beta\]
* Bandcamp \[beta\]
<!-- Hidden span to keep old links compatible. --> <!-- Hidden span to keep old links compatible. -->
<span id="updates"></span> <span id="updates"></span>

View File

@ -79,6 +79,7 @@ NewPipe wuxuu taageeraa adeegyo badan. [warqadan](https://teamnewpipe.github.io/
* SoundCloud \[tijaabo\] * SoundCloud \[tijaabo\]
* media.ccc.de \[tijaabo\] * media.ccc.de \[tijaabo\]
* PeerTube instances \[tijaabo\] * PeerTube instances \[tijaabo\]
* Bandcamp \[tijaabo\]
## Kushubida iyo cusboonaysiinta ## Kushubida iyo cusboonaysiinta
Marka koodhka NewPipe isbadal ku dhaco (wax cusub oo lagusoo kordhiyay ama cilad bixin), ugu dambayn waxaa lasii daayaa mid cusub (Siidayn). Siidaynta qaabkeedu waa x.xx.x . Si aad midka cusub u hesho, waxaad samayn kartaa: Marka koodhka NewPipe isbadal ku dhaco (wax cusub oo lagusoo kordhiyay ama cilad bixin), ugu dambayn waxaa lasii daayaa mid cusub (Siidayn). Siidaynta qaabkeedu waa x.xx.x . Si aad midka cusub u hesho, waxaad samayn kartaa:

View File

@ -180,7 +180,7 @@ dependencies {
// NewPipe dependencies // NewPipe dependencies
// You can use a local version by uncommenting a few lines in settings.gradle // You can use a local version by uncommenting a few lines in settings.gradle
implementation "com.github.TeamNewPipe:NewPipeExtractor:7e6f464407fc1a2c8fb0886d294093526a6ef0f1" implementation 'com.github.TeamNewPipe:NewPipeExtractor:def745b801b2ef35c1a0fee1be950331ca6a0cd2'
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
implementation "org.jsoup:jsoup:1.13.1" implementation "org.jsoup:jsoup:1.13.1"

View File

@ -317,6 +317,22 @@
<data android:pathPrefix="/accounts/" /> <data android:pathPrefix="/accounts/" />
<data android:pathPrefix="/video-channels/" /> <data android:pathPrefix="/video-channels/" />
</intent-filter> </intent-filter>
<!-- Bandcamp filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="bandcamp.com"/>
<data android:host="*.bandcamp.com"/>
<data android:pathPrefix="/"/>
</intent-filter>
</activity> </activity>
<service <service
android:name=".RouterActivity$FetcherService" android:name=".RouterActivity$FetcherService"

View File

@ -62,6 +62,7 @@ import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.Stream;
@ -1547,8 +1548,19 @@ public final class VideoDetailFragment
} }
if (!info.getErrors().isEmpty()) { if (!info.getErrors().isEmpty()) {
showSnackBarError(new ErrorInfo(info.getErrors(), // Bandcamp fan pages are not yet supported and thus a ContentNotAvailableException is
UserAction.REQUESTED_STREAM, info.getUrl(), info)); // thrown. This is not an error and thus should not be shown to the user.
for (final Throwable throwable : info.getErrors()) {
if (throwable instanceof ContentNotSupportedException
&& "Fan pages are not supported".equals(throwable.getMessage())) {
info.getErrors().remove(throwable);
}
}
if (!info.getErrors().isEmpty()) {
showSnackBarError(new ErrorInfo(info.getErrors(),
UserAction.REQUESTED_STREAM, info.getUrl(), info));
}
} }
binding.detailControlsDownload.setVisibility(info.getStreamType() == StreamType.LIVE_STREAM binding.detailControlsDownload.setVisibility(info.getStreamType() == StreamType.LIVE_STREAM

View File

@ -1129,6 +1129,12 @@ public final class Player implements
// Close it because when changing orientation from portrait // Close it because when changing orientation from portrait
// (in fullscreen mode) the size of queue layout can be larger than the screen size // (in fullscreen mode) the size of queue layout can be larger than the screen size
closeItemsList(); closeItemsList();
// When the orientation changed, the screen height might be smaller.
// If the end screen thumbnail is not re-scaled,
// it can be larger than the current screen height
// and thus enlarging the whole player.
// This causes the seekbar to be ouf the visible area.
updateEndScreenThumbnail();
break; break;
case Intent.ACTION_SCREEN_ON: case Intent.ACTION_SCREEN_ON:
// Interrupt playback only when screen turns on // Interrupt playback only when screen turns on
@ -1187,6 +1193,78 @@ public final class Player implements
.loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this); .loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this);
} }
/**
* Scale the player audio / end screen thumbnail down if necessary.
* <p>
* This is necessary when the thumbnail's height is larger than the device's height
* and thus is enlarging the player's height
* causing the bottom playback controls to be out of the visible screen.
* </p>
*/
public void updateEndScreenThumbnail() {
if (currentThumbnail == null) {
return;
}
final float endScreenHeight = calculateMaxEndScreenThumbnailHeight();
final Bitmap endScreenBitmap = Bitmap.createScaledBitmap(
currentThumbnail,
(int) (currentThumbnail.getWidth()
/ (currentThumbnail.getHeight() / endScreenHeight)),
(int) endScreenHeight,
true);
if (DEBUG) {
Log.d(TAG, "Thumbnail - updateEndScreenThumbnail() called with: "
+ "currentThumbnail = [" + currentThumbnail + "], "
+ currentThumbnail.getWidth() + "x" + currentThumbnail.getHeight()
+ ", scaled end screen height = " + endScreenHeight
+ ", scaled end screen width = " + endScreenBitmap.getWidth());
}
binding.endScreen.setImageBitmap(endScreenBitmap);
}
/**
* Calculate the maximum allowed height for the {@link R.id.endScreen}
* to prevent it from enlarging the player.
* <p>
* The calculating follows these rules:
* <ul>
* <li>
* Show at least stream title and content creator on TVs and tablets
* when in landscape (always the case for TVs) and not in fullscreen mode.
* This requires to have at least <code>85dp</code> free space for {@link R.id.detail_root}
* and additional space for the stream title text size
* ({@link R.id.detail_title_root_layout}).
* The text size is <code>15sp</code> on tablets and <code>16sp</code> on TVs,
* see {@link R.id.titleTextView}.
* </li>
* <li>
* Otherwise, the max thumbnail height is the screen height.
* </li>
* </ul>
*
* @return the maximum height for the end screen thumbnail
*/
private float calculateMaxEndScreenThumbnailHeight() {
// ensure that screenHeight is initialized and thus not 0
updateScreenSize();
if (DeviceUtils.isTv(context) && !isFullscreen) {
final int videoInfoHeight =
DeviceUtils.dpToPx(85, context) + DeviceUtils.spToPx(16, context);
return Math.min(currentThumbnail.getHeight(), screenHeight - videoInfoHeight);
} else if (DeviceUtils.isTablet(context) && service.isLandscape() && !isFullscreen) {
final int videoInfoHeight =
DeviceUtils.dpToPx(85, context) + DeviceUtils.spToPx(15, context);
return Math.min(currentThumbnail.getHeight(), screenHeight - videoInfoHeight);
} else { // fullscreen player: max height is the device height
return Math.min(currentThumbnail.getHeight(), screenHeight);
}
}
@Override @Override
public void onLoadingStarted(final String imageUri, final View view) { public void onLoadingStarted(final String imageUri, final View view) {
if (DEBUG) { if (DEBUG) {
@ -1207,23 +1285,29 @@ public final class Player implements
@Override @Override
public void onLoadingComplete(final String imageUri, final View view, public void onLoadingComplete(final String imageUri, final View view,
final Bitmap loadedImage) { final Bitmap loadedImage) {
final float width = Math.min( // scale down the notification thumbnail for performance
final float notificationThumbnailWidth = Math.min(
context.getResources().getDimension(R.dimen.player_notification_thumbnail_width), context.getResources().getDimension(R.dimen.player_notification_thumbnail_width),
loadedImage.getWidth()); loadedImage.getWidth());
currentThumbnail = Bitmap.createScaledBitmap(
loadedImage,
(int) notificationThumbnailWidth,
(int) (loadedImage.getHeight()
/ (loadedImage.getWidth() / notificationThumbnailWidth)),
true);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Thumbnail - onLoadingComplete() called with: " Log.d(TAG, "Thumbnail - onLoadingComplete() called with: "
+ "imageUri = [" + imageUri + "], view = [" + view + "], " + "imageUri = [" + imageUri + "], view = [" + view + "], "
+ "loadedImage = [" + loadedImage + "], " + "loadedImage = [" + loadedImage + "], "
+ loadedImage.getWidth() + "x" + loadedImage.getHeight() + loadedImage.getWidth() + "x" + loadedImage.getHeight()
+ ", scaled width = " + width); + ", scaled notification width = " + notificationThumbnailWidth);
} }
currentThumbnail = Bitmap.createScaledBitmap(loadedImage,
(int) width,
(int) (loadedImage.getHeight() / (loadedImage.getWidth() / width)), true);
binding.endScreen.setImageBitmap(loadedImage);
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
// there is a new thumbnail, thus the end screen thumbnail needs to be changed, too.
updateEndScreenThumbnail();
} }
@Override @Override

View File

@ -6,8 +6,10 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.Build; import android.os.Build;
import android.util.TypedValue;
import android.view.KeyEvent; import android.view.KeyEvent;
import androidx.annotation.Dimension;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@ -70,4 +72,20 @@ public final class DeviceUtils {
return false; return false;
} }
} }
public static int dpToPx(@Dimension(unit = Dimension.DP) final int dp,
@NonNull final Context context) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
context.getResources().getDisplayMetrics());
}
public static int spToPx(@Dimension(unit = Dimension.SP) final int sp,
@NonNull final Context context) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
sp,
context.getResources().getDisplayMetrics());
}
} }

View File

@ -48,6 +48,10 @@ public final class KioskTranslator {
return c.getString(R.string.recent); return c.getString(R.string.recent);
case "live": case "live":
return c.getString(R.string.duration_live); return c.getString(R.string.duration_live);
case "Featured":
return c.getString(R.string.featured);
case "Radio":
return c.getString(R.string.radio);
default: default:
return kioskId; return kioskId;
} }
@ -69,6 +73,10 @@ public final class KioskTranslator {
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_thumb_up); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_thumb_up);
case "live": case "live":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_live_tv); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_live_tv);
case "Featured":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_stars);
case "Radio":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_radio);
default: default:
return 0; return 0;
} }

View File

@ -38,6 +38,8 @@ public final class ServiceHelper {
return R.drawable.place_holder_gadse; return R.drawable.place_holder_gadse;
case 3: case 3:
return R.drawable.place_holder_peertube; return R.drawable.place_holder_peertube;
case 4:
return R.drawable.place_holder_bandcamp;
default: default:
return R.drawable.place_holder_circle; return R.drawable.place_holder_circle;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -44,4 +44,13 @@
<color name="dark_media_ccc_accent_color">#FFFFFF</color> <color name="dark_media_ccc_accent_color">#FFFFFF</color>
<color name="dark_media_ccc_statusbar_color">#9e9e9e</color> <color name="dark_media_ccc_statusbar_color">#9e9e9e</color>
<!-- Bandcamp -->
<color name="light_bandcamp_primary_color">#17a0c4</color>
<color name="light_bandcamp_accent_color">#000000</color>
<color name="light_bandcamp_statusbar_color">#17a0c4</color>
<color name="dark_bandcamp_primary_color">#17a0c4</color>
<color name="dark_bandcamp_accent_color">#FFFFFF</color>
<color name="dark_bandcamp_statusbar_color">#17a0c4</color>
</resources> </resources>

View File

@ -707,4 +707,6 @@
<string name="private_content">This content is private, so it cannot be streamed or downloaded by NewPipe.</string> <string name="private_content">This content is private, so it cannot be streamed or downloaded by NewPipe.</string>
<string name="youtube_music_premium_content">This video is available only to YouTube Music Premium members, so it cannot be streamed or downloaded by NewPipe.</string> <string name="youtube_music_premium_content">This video is available only to YouTube Music Premium members, so it cannot be streamed or downloaded by NewPipe.</string>
<string name="paid_content">This content is only available to users who have paid, so it cannot be streamed or downloaded by NewPipe.</string> <string name="paid_content">This content is only available to users who have paid, so it cannot be streamed or downloaded by NewPipe.</string>
<string name="featured">Featured</string>
<string name="radio">Radio</string>
</resources> </resources>

View File

@ -70,4 +70,23 @@
<item name="colorAccent">@color/dark_media_ccc_accent_color</item> <item name="colorAccent">@color/dark_media_ccc_accent_color</item>
</style> </style>
<!-- Bandcamp -->
<style name="LightTheme.Bandcamp" parent="LightTheme">
<item name="colorPrimary">@color/light_bandcamp_primary_color</item>
<item name="colorPrimaryDark">@color/light_bandcamp_statusbar_color</item>
<item name="colorAccent">@color/light_bandcamp_accent_color</item>
</style>
<style name="DarkTheme.Bandcamp" parent="DarkTheme">
<item name="colorPrimary">@color/dark_bandcamp_primary_color</item>
<item name="colorPrimaryDark">@color/dark_bandcamp_statusbar_color</item>
<item name="colorAccent">@color/dark_bandcamp_accent_color</item>
</style>
<style name="BlackTheme.Bandcamp" parent="BlackTheme">
<item name="colorPrimary">@color/dark_bandcamp_primary_color</item>
<item name="colorPrimaryDark">@color/dark_bandcamp_statusbar_color</item>
<item name="colorAccent">@color/dark_bandcamp_accent_color</item>
</style>
</resources> </resources>