🔀 Merge some services from spotube
This commit is contained in:
12
lib/services/sourced_track/models/search.dart
Normal file
12
lib/services/sourced_track/models/search.dart
Normal file
@ -0,0 +1,12 @@
|
||||
enum SearchMode {
|
||||
youtube._("YouTube"),
|
||||
youtubeMusic._("YouTube Music");
|
||||
|
||||
final String label;
|
||||
|
||||
const SearchMode._(this.label);
|
||||
|
||||
factory SearchMode.fromString(String key) {
|
||||
return SearchMode.values.firstWhere((e) => e.name == key);
|
||||
}
|
||||
}
|
33
lib/services/sourced_track/models/source_info.dart
Executable file
33
lib/services/sourced_track/models/source_info.dart
Executable file
@ -0,0 +1,33 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'source_info.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class SourceInfo {
|
||||
final String id;
|
||||
final String title;
|
||||
final String artist;
|
||||
final String artistUrl;
|
||||
final String? album;
|
||||
|
||||
final String thumbnail;
|
||||
final String pageUrl;
|
||||
|
||||
final Duration duration;
|
||||
|
||||
SourceInfo({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.artist,
|
||||
required this.thumbnail,
|
||||
required this.pageUrl,
|
||||
required this.duration,
|
||||
required this.artistUrl,
|
||||
this.album,
|
||||
});
|
||||
|
||||
factory SourceInfo.fromJson(Map<String, dynamic> json) =>
|
||||
_$SourceInfoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SourceInfoToJson(this);
|
||||
}
|
30
lib/services/sourced_track/models/source_info.g.dart
Executable file
30
lib/services/sourced_track/models/source_info.g.dart
Executable file
@ -0,0 +1,30 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'source_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SourceInfo _$SourceInfoFromJson(Map json) => SourceInfo(
|
||||
id: json['id'] as String,
|
||||
title: json['title'] as String,
|
||||
artist: json['artist'] as String,
|
||||
thumbnail: json['thumbnail'] as String,
|
||||
pageUrl: json['pageUrl'] as String,
|
||||
duration: Duration(microseconds: json['duration'] as int),
|
||||
artistUrl: json['artistUrl'] as String,
|
||||
album: json['album'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SourceInfoToJson(SourceInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'artist': instance.artist,
|
||||
'artistUrl': instance.artistUrl,
|
||||
'album': instance.album,
|
||||
'thumbnail': instance.thumbnail,
|
||||
'pageUrl': instance.pageUrl,
|
||||
'duration': instance.duration.inMicroseconds,
|
||||
};
|
58
lib/services/sourced_track/models/source_map.dart
Executable file
58
lib/services/sourced_track/models/source_map.dart
Executable file
@ -0,0 +1,58 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:rhythm_box/services/sourced_track/enums.dart';
|
||||
|
||||
part 'source_map.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class SourceQualityMap {
|
||||
final String high;
|
||||
final String medium;
|
||||
final String low;
|
||||
|
||||
const SourceQualityMap({
|
||||
required this.high,
|
||||
required this.medium,
|
||||
required this.low,
|
||||
});
|
||||
|
||||
factory SourceQualityMap.fromJson(Map<String, dynamic> json) =>
|
||||
_$SourceQualityMapFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SourceQualityMapToJson(this);
|
||||
|
||||
operator [](SourceQualities key) {
|
||||
switch (key) {
|
||||
case SourceQualities.high:
|
||||
return high;
|
||||
case SourceQualities.medium:
|
||||
return medium;
|
||||
case SourceQualities.low:
|
||||
return low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SourceMap {
|
||||
final SourceQualityMap? weba;
|
||||
final SourceQualityMap? m4a;
|
||||
|
||||
const SourceMap({
|
||||
this.weba,
|
||||
this.m4a,
|
||||
});
|
||||
|
||||
factory SourceMap.fromJson(Map<String, dynamic> json) =>
|
||||
_$SourceMapFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SourceMapToJson(this);
|
||||
|
||||
operator [](SourceCodecs key) {
|
||||
switch (key) {
|
||||
case SourceCodecs.weba:
|
||||
return weba;
|
||||
case SourceCodecs.m4a:
|
||||
return m4a;
|
||||
}
|
||||
}
|
||||
}
|
36
lib/services/sourced_track/models/source_map.g.dart
Executable file
36
lib/services/sourced_track/models/source_map.g.dart
Executable file
@ -0,0 +1,36 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'source_map.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SourceQualityMap _$SourceQualityMapFromJson(Map json) => SourceQualityMap(
|
||||
high: json['high'] as String,
|
||||
medium: json['medium'] as String,
|
||||
low: json['low'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SourceQualityMapToJson(SourceQualityMap instance) =>
|
||||
<String, dynamic>{
|
||||
'high': instance.high,
|
||||
'medium': instance.medium,
|
||||
'low': instance.low,
|
||||
};
|
||||
|
||||
SourceMap _$SourceMapFromJson(Map json) => SourceMap(
|
||||
weba: json['weba'] == null
|
||||
? null
|
||||
: SourceQualityMap.fromJson(
|
||||
Map<String, dynamic>.from(json['weba'] as Map)),
|
||||
m4a: json['m4a'] == null
|
||||
? null
|
||||
: SourceQualityMap.fromJson(
|
||||
Map<String, dynamic>.from(json['m4a'] as Map)),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SourceMapToJson(SourceMap instance) => <String, dynamic>{
|
||||
'weba': instance.weba?.toJson(),
|
||||
'm4a': instance.m4a?.toJson(),
|
||||
};
|
115
lib/services/sourced_track/models/video_info.dart
Executable file
115
lib/services/sourced_track/models/video_info.dart
Executable file
@ -0,0 +1,115 @@
|
||||
import 'package:piped_client/piped_client.dart';
|
||||
import 'package:rhythm_box/services/sourced_track/models/search.dart';
|
||||
|
||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||
|
||||
class YoutubeVideoInfo {
|
||||
final SearchMode searchMode;
|
||||
final String title;
|
||||
final Duration duration;
|
||||
final String thumbnailUrl;
|
||||
final String id;
|
||||
final int likes;
|
||||
final int dislikes;
|
||||
final int views;
|
||||
final String channelName;
|
||||
final String channelId;
|
||||
final DateTime publishedAt;
|
||||
|
||||
YoutubeVideoInfo({
|
||||
required this.searchMode,
|
||||
required this.title,
|
||||
required this.duration,
|
||||
required this.thumbnailUrl,
|
||||
required this.id,
|
||||
required this.likes,
|
||||
required this.dislikes,
|
||||
required this.views,
|
||||
required this.channelName,
|
||||
required this.publishedAt,
|
||||
required this.channelId,
|
||||
});
|
||||
|
||||
YoutubeVideoInfo.fromJson(Map<String, dynamic> json)
|
||||
: title = json['title'],
|
||||
searchMode = SearchMode.fromString(json['searchMode']),
|
||||
duration = Duration(seconds: json['duration']),
|
||||
thumbnailUrl = json['thumbnailUrl'],
|
||||
id = json['id'],
|
||||
likes = json['likes'],
|
||||
dislikes = json['dislikes'],
|
||||
views = json['views'],
|
||||
channelName = json['channelName'],
|
||||
channelId = json['channelId'],
|
||||
publishedAt = DateTime.tryParse(json['publishedAt']) ?? DateTime.now();
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'title': title,
|
||||
'duration': duration.inSeconds,
|
||||
'thumbnailUrl': thumbnailUrl,
|
||||
'id': id,
|
||||
'likes': likes,
|
||||
'dislikes': dislikes,
|
||||
'views': views,
|
||||
'channelName': channelName,
|
||||
'channelId': channelId,
|
||||
'publishedAt': publishedAt.toIso8601String(),
|
||||
'searchMode': searchMode.name,
|
||||
};
|
||||
|
||||
factory YoutubeVideoInfo.fromVideo(Video video) {
|
||||
return YoutubeVideoInfo(
|
||||
searchMode: SearchMode.youtube,
|
||||
title: video.title,
|
||||
duration: video.duration ?? Duration.zero,
|
||||
thumbnailUrl: video.thumbnails.mediumResUrl,
|
||||
id: video.id.value,
|
||||
likes: video.engagement.likeCount ?? 0,
|
||||
dislikes: video.engagement.dislikeCount ?? 0,
|
||||
views: video.engagement.viewCount,
|
||||
channelName: video.author,
|
||||
channelId: '/c/${video.channelId.value}',
|
||||
publishedAt: video.uploadDate ?? DateTime(2003, 9, 9),
|
||||
);
|
||||
}
|
||||
|
||||
factory YoutubeVideoInfo.fromSearchItemStream(
|
||||
PipedSearchItemStream searchItem,
|
||||
SearchMode searchMode,
|
||||
) {
|
||||
return YoutubeVideoInfo(
|
||||
searchMode: searchMode,
|
||||
title: searchItem.title,
|
||||
duration: searchItem.duration,
|
||||
thumbnailUrl: searchItem.thumbnail,
|
||||
id: searchItem.id,
|
||||
likes: 0,
|
||||
dislikes: 0,
|
||||
views: searchItem.views,
|
||||
channelName: searchItem.uploaderName,
|
||||
channelId: searchItem.uploaderUrl ?? "",
|
||||
publishedAt: searchItem.uploadedDate != null
|
||||
? DateTime.tryParse(searchItem.uploadedDate!) ?? DateTime(2003, 9, 9)
|
||||
: DateTime(2003, 9, 9),
|
||||
);
|
||||
}
|
||||
|
||||
factory YoutubeVideoInfo.fromStreamResponse(
|
||||
PipedStreamResponse stream, SearchMode searchMode) {
|
||||
return YoutubeVideoInfo(
|
||||
searchMode: searchMode,
|
||||
title: stream.title,
|
||||
duration: stream.duration,
|
||||
thumbnailUrl: stream.thumbnailUrl,
|
||||
id: stream.id,
|
||||
likes: stream.likes,
|
||||
dislikes: stream.dislikes,
|
||||
views: stream.views,
|
||||
channelName: stream.uploader,
|
||||
publishedAt: stream.uploadedDate != null
|
||||
? DateTime.tryParse(stream.uploadedDate!) ?? DateTime(2003, 9, 9)
|
||||
: DateTime(2003, 9, 9),
|
||||
channelId: stream.uploaderUrl,
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user